AssistantCoach / app.py
Rahaf2001's picture
Update app.py
9f2c107 verified
# 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()