Spaces:
Sleeping
Sleeping
| # app.py | |
| import gradio as gr | |
| from typing import List, Literal, Tuple | |
| # ------------------------------- | |
| # Motivation banner (rotates every 10s via JS) | |
| # ------------------------------- | |
| MOTIVATION_JS = """ | |
| <div id="motivation" class="motivation">Letβs begin strong! π</div> | |
| <script> | |
| const MOTIVATION = [ | |
| "Youβre doing greatβone step at a time! πͺ", | |
| "Small habits, big results. Keep going. π", | |
| "Hydrate and moveβyour future self thanks you. π§", | |
| "Form first, weight second. Youβve got this. π§ ", | |
| "Consistency beats intensity. Show up today. β ", | |
| ]; | |
| let i = 0; | |
| function tick(){ | |
| const el = document.getElementById("motivation"); | |
| if (!el) return; | |
| el.textContent = MOTIVATION[i % MOTIVATION.length]; | |
| i++; | |
| } | |
| tick(); // first line immediately | |
| setInterval(tick, 10000); // then every 10s | |
| </script> | |
| """ | |
| # ------------------------------- | |
| # 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() | |