# app.py import gradio as gr from typing import List, Literal, Tuple # ------------------------------- # Motivation banner (rotates every 10s via JS) # ------------------------------- MOTIVATION_JS = """
Let’s begin strong! 🚀
""" # ------------------------------- # Helpers: BMI / BMR / TDEE # ------------------------------- def bmi_value(height_cm: float, weight_kg: float) -> float: if height_cm <= 0 or weight_kg <= 0: return 0.0 h_m = height_cm / 100.0 return round(weight_kg / (h_m ** 2), 2) def bmi_category(bmi: float) -> str: if bmi == 0: return "Invalid inputs" if bmi < 18.5: return "Underweight" if bmi < 25: return "Normal" if bmi < 30: return "Overweight" return "Obese" def bmr_mifflin(sex: Literal["male","female"], age: int, height_cm: float, weight_kg: float) -> float: # Mifflin–St Jeor s = 5 if sex == "male" else -161 return 10*weight_kg + 6.25*height_cm - 5*age + s ACTIVITY = { "Sedentary (office/no exercise)": 1.2, "Light (1-3x/wk)": 1.375, "Moderate (3-5x/wk)": 1.55, "Active (6-7x/wk)": 1.725, "Athlete (2x/day)": 1.9, } def tdee(sex: str, age: int, height_cm: float, weight_kg: float, activity: str) -> Tuple[int, int, int]: base = bmr_mifflin(sex, age, height_cm, weight_kg) factor = ACTIVITY.get(activity, 1.2) maintenance = round(base * factor) cut = maintenance - 500 # gentle fat loss bulk = maintenance + 300 # lean bulk return maintenance, cut, bulk def water_intake_ml(weight_kg: float) -> int: # 35 ml per kg (simple guideline) return int(weight_kg * 35) # ------------------------------- # Agent 1: BMI & TDEE # ------------------------------- def agent_bmi_tdee(sex, age, height_cm, weight_kg, activity, goal): bmi = bmi_value(height_cm, weight_kg) cat = bmi_category(bmi) maint, cut, bulk = tdee(sex, age, height_cm, weight_kg, activity) target = {"Lose fat": cut, "Maintain": maint, "Build muscle": bulk}[goal] water = water_intake_ml(weight_kg) water_l = round(water / 1000, 1) md = f"""### 📊 BMI & TDEE - **BMI:** `{bmi}` → **{cat}** - **Maintenance Calories:** **{maint} kcal/day** - **Target for goal ({goal}):** **{target} kcal/day** - **Water recommendation:** ~ **{water_l} L/day** (≈ {water} ml) """ return md # ------------------------------- # Agent 2: Workout Plan Generator # ------------------------------- GOAL_SPLITS = { "Lose fat": ["FBW", "FBW", "LISS Cardio", "Mobility"], "Maintain": ["Upper", "Lower", "Full Body", "HIIT/Cardio", "Mobility"], "Build muscle": ["Push", "Pull", "Legs", "Upper", "Lower", "Mobility"], } def session_template(name: str, level: str, equipment: str) -> List[str]: # Compact, beginner-friendly templates if name == "FBW": return [ "Goblet Squat 3x8-10", "Push-ups (incline if needed) 3x6-10", "Dumbbell Row 3x10", "Hip Hinge (RDL/Good Morning) 3x10", "Plank 3x30-45s", ] if name == "Upper": return [ "DB Bench Press 3x8-10", "One-arm Row 3x10/side", "DB Shoulder Press 3x10", "Lat Pulldown or Assisted Pull 3x8-10", "Facepull / Band Pull-apart 3x12-15", ] if name == "Lower": return [ "Squat pattern 4x6-10", "Hinge pattern 3x8-10", "Split Squat/Lunge 3x8/leg", "Calf Raise 3x12-15", "Core: Deadbug 3x10", ] if name == "Push": return [ "DB Bench Press 4x6-10", "Incline Push-ups 3x10", "DB Shoulder Press 3x8-10", "Lateral Raise 3x12-15", "Triceps Extensions 3x10-12", ] if name == "Pull": return [ "Lat Pulldown / Assisted Pull-ups 4x6-10", "Seated Row / One-arm Row 3x10", "Rear Delt Raise 3x12-15", "DB Curl 3x10-12", "Back Extension 3x12", ] if name == "Legs": return [ "Back/Front/Goblet Squat 4x6-10", "Romanian Deadlift 3x8-10", "Leg Press or Step-ups 3x10", "Hamstring Curl 3x10-12", "Core: Side Plank 3x30s/side", ] if name == "HIIT/Cardio": return ["20–25 min intervals (1 fast / 1 easy) OR 30–40 min brisk walk"] if name == "LISS Cardio": return ["30–45 min easy jog/bike/walk (Zone 2)"] if name == "Mobility": return ["15–20 min mobility + light stretching & breathing"] return ["Rest / Light activity (walk 6–8k steps)"] def agent_workout(level: str, days_per_week: int, goal: str, equipment: str): days_per_week = max(2, min(6, int(days_per_week))) base = GOAL_SPLITS[goal] plan = [] for i in range(days_per_week): name = base[i % len(base)] items = session_template(name, level, equipment) plan.append((f"Day {i+1} — {name}", items)) md = "### 🏋️ Weekly Plan\n" for title, items in plan: md += f"\n**{title}**\n" + "\n".join([f"- {x}" for x in items]) + "\n" md += "\n*Rest 60–90s between sets. Aim for ~2 reps in reserve (RIR 2).*" return md # ------------------------------- # Agent 3: Nutrition & Meal Plan # ------------------------------- PROTEIN_PER_KG = 1.6 # g/kg for beginners FAT_MIN_G_PER_KG = 0.6 # g/kg def macros_for_calories(weight_kg: float, calories: int) -> Tuple[int, int, int]: protein_g = int(round(PROTEIN_PER_KG * weight_kg)) fat_g = int(round(FAT_MIN_G_PER_KG * weight_kg)) cals_from_p = protein_g * 4 cals_from_f = fat_g * 9 carbs_g = max(0, int(round((calories - cals_from_p - cals_from_f) / 4))) return protein_g, carbs_g, fat_g def filter_foods(options: List[str], avoid: List[str]) -> List[str]: avoid_set = {a.strip().lower() for a in avoid if a.strip()} res = [] for o in options: if any(bad in o.lower() for bad in avoid_set): continue res.append(o) return res DEFAULT_PROTEINS = ["chicken breast", "eggs", "tuna", "lean beef", "laban protein", "tofu"] DEFAULT_CARBS = ["rice", "oats", "potatoes", "pasta", "whole-wheat bread", "dates", "fruit"] DEFAULT_FATS = ["olive oil", "avocado", "nuts", "tahini", "peanut butter"] DEFAULT_VEGS = ["salad mix", "cucumber", "tomato", "broccoli", "spinach"] def agent_nutrition(weight_kg: float, target_kcal: int, liked_csv: str, avoid_csv: str, meals: int): liked = [x.strip() for x in liked_csv.split(",") if x.strip()] avoid = [x.strip() for x in avoid_csv.split(",") if x.strip()] proteins = filter_foods((liked or DEFAULT_PROTEINS), avoid) carbs = filter_foods(DEFAULT_CARBS, avoid) fats = filter_foods(DEFAULT_FATS, avoid) vegs = filter_foods(DEFAULT_VEGS, avoid) p, c, f = macros_for_calories(weight_kg, target_kcal) per_meal = max(1, meals) p_m, c_m, f_m = p // per_meal, c // per_meal, f // per_meal sample_day = [ f"Meal {i+1}: {proteins[i % len(proteins)]} + {carbs[i % len(carbs)]} " f"+ {vegs[i % len(vegs)]} + {fats[i % len(fats)]}" for i in range(per_meal) ] md = f"""### 🥗 Nutrition Plan - **Target:** **{target_kcal} kcal/day** - **Macros (approx.):** Protein **{p} g**, Carbs **{c} g**, Fat **{f} g** → per meal ≈ **{p_m}/{c_m}/{f_m} g** - **Preferred foods used:** {", ".join(proteins[:3])} … **Sample Day:** {chr(10).join([f"- {m}" for m in sample_day])} **Grocery list (starter):** - Protein: {", ".join(proteins)} - Carbs: {", ".join(carbs)} - Fats: {", ".join(fats)} - Veggies: {", ".join(vegs)} > Tips: add 20–30 g protein each meal, plenty of veggies, and 1–2 pieces of fruit/day. """ return md # ------------------------------- # Chained: All-in-One Coach # ------------------------------- def coach_all_in_one( sex, age, height_cm, weight_kg, activity, goal, level, days, equipment, liked_csv, avoid_csv, meals ): report = agent_bmi_tdee(sex, age, height_cm, weight_kg, activity, goal) maint, cut, bulk = tdee(sex, age, height_cm, weight_kg, activity) target = {"Lose fat": cut, "Maintain": maint, "Build muscle": bulk}[goal] w = agent_workout(level, days, goal, equipment) n = agent_nutrition(weight_kg, target, liked_csv, avoid_csv, meals) return report + "\n---\n" + w + "\n---\n" + n # ------------------------------- # UI # ------------------------------- with gr.Blocks( title="Beginner Gym Coach — Multi-Agent", css=""" .motivation{ background:#fff6e5; border-left:6px solid #ffa84b; padding:10px 14px; border-radius:8px; font-size:16px; margin-bottom:10px; } """ ) as demo: gr.Markdown("## 🏋️ Beginner Gym Coach — Multi-Agent\nGive me your basics and I’ll coach you.") gr.HTML(MOTIVATION_JS) with gr.Tabs(): # -------- BMI & TDEE -------- with gr.Tab("BMI & TDEE"): with gr.Row(): with gr.Column(scale=1): sex = gr.Radio(["male","female"], value="female", label="Sex") age = gr.Slider(15, 70, value=24, step=1, label="Age") height = gr.Slider(130, 210, value=165, step=1, label="Height (cm)") weight = gr.Slider(35, 160, value=60, step=0.5, label="Weight (kg)") activity = gr.Dropdown(list(ACTIVITY.keys()), value="Light (1-3x/wk)", label="Activity") goal = gr.Radio(["Lose fat","Maintain","Build muscle"], value="Maintain", label="Goal") btn1 = gr.Button("Calculate") with gr.Column(scale=1): out1 = gr.Markdown() btn1.click(agent_bmi_tdee, [sex, age, height, weight, activity, goal], out1) # -------- Workout Plan -------- with gr.Tab("Workout Plan"): with gr.Row(): with gr.Column(scale=1): level = gr.Radio(["Beginner","Intermediate"], value="Beginner", label="Level") days = gr.Slider(2, 6, value=4, step=1, label="Days/week") goal_w = gr.Radio(["Lose fat","Maintain","Build muscle"], value="Build muscle", label="Goal") equipment = gr.Dropdown(["Gym (machines/dumbbells)", "Home (bands/db)", "Bodyweight only"], value="Gym (machines/dumbbells)", label="Equipment") btn2 = gr.Button("Generate Plan") with gr.Column(scale=1): out2 = gr.Markdown() btn2.click(agent_workout, [level, days, goal_w, equipment], out2) # -------- Nutrition -------- with gr.Tab("Nutrition"): with gr.Row(): with gr.Column(scale=1): target_kcal = gr.Number(value=1900, label="Target calories (kcal)") weight_n = gr.Number(value=60, label="Weight (kg)") liked = gr.Textbox(label="Preferred foods (comma-separated)", placeholder="chicken, eggs, rice, oats, salad, laban") avoid = gr.Textbox(label="Allergies / avoid (comma-separated)", placeholder="nuts, shrimp, gluten") meals = gr.Slider(2, 6, value=3, step=1, label="Meals/day") btn3 = gr.Button("Build Meal Plan") with gr.Column(scale=1): out3 = gr.Markdown() btn3.click(agent_nutrition, [weight_n, target_kcal, liked, avoid, meals], out3) # -------- All-in-One -------- with gr.Tab("All-in-One Coach"): with gr.Row(): with gr.Column(scale=1): sex2 = gr.Radio(["male","female"], value="female", label="Sex") age2 = gr.Slider(15, 70, value=24, step=1, label="Age") height2 = gr.Slider(130, 210, value=165, step=1, label="Height (cm)") weight2 = gr.Slider(35, 160, value=60, step=0.5, label="Weight (kg)") activity2 = gr.Dropdown(list(ACTIVITY.keys()), value="Light (1-3x/wk)", label="Activity") goal2 = gr.Radio(["Lose fat","Maintain","Build muscle"], value="Build muscle", label="Goal") level2 = gr.Radio(["Beginner","Intermediate"], value="Beginner", label="Level") days2 = gr.Slider(2, 6, value=4, step=1, label="Days/week") equipment2 = gr.Dropdown(["Gym (machines/dumbbells)", "Home (bands/db)", "Bodyweight only"], value="Gym (machines/dumbbells)", label="Equipment") liked2 = gr.Textbox(label="Preferred foods", placeholder="chicken, eggs, rice, potatoes") avoid2 = gr.Textbox(label="Allergies / avoid", placeholder="nuts, shrimp") meals2 = gr.Slider(2, 6, value=3, step=1, label="Meals/day") btn4 = gr.Button("Get My Full Plan 🚀") with gr.Column(scale=1): out4 = gr.Markdown() btn4.click( coach_all_in_one, [sex2, age2, height2, weight2, activity2, goal2, level2, days2, equipment2, liked2, avoid2, meals2], out4 ) if __name__ == "__main__": demo.launch()