File size: 23,119 Bytes
5c76a88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503



# ==============================================================================
# ENHANCED APP: AI-Powered Fitness Coach
# ==============================================================================
#
# Author: Your Name (Originally by [Original Author Name])
# Bootcamp: GenAI Bootcamp
# Project: Showcase Project
#
# Description:
# This Gradio application serves as a multi-agent AI fitness coach. It provides
# personalized recommendations for BMI/TDEE, workout plans, and nutrition.
#
# Enhancements in this version:
#   1.  **Code Refactoring & Readability:** Improved structure, comments, and type hints.
#   2.  **AI Coach Persona:** A new "persona" layer that delivers advice in a more
#       conversational and encouraging tone, simulating a real coach.
#   3.  **Improved UI/UX:** More polished Markdown outputs for a better user experience.
#   4.  **Modular & Scalable:** Clear separation of concerns between data calculation
#       (helpers), plan generation (agents), and user interface (Gradio).
#   5.  **Educational Comments:** Added comments to explain key concepts and formulas,
#       useful for the project presentation.
#
# ==============================================================================

import gradio as gr
from typing import List, Literal, Tuple

# ==============================================================================
# SECTION 1: CONFIGURATION & CONSTANTS
# ==============================================================================

# --- Motivation Banner (JavaScript) ---
# This JS snippet rotates motivational quotes in the UI to keep users engaged.
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();
  setInterval(tick, 10000);
</script>
"""

# --- Activity Level Multipliers ---
# These values are used to calculate TDEE based on the Mifflin-St Jeor formula.
ACTIVITY_MULTIPLIERS = {
    "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,
}

# --- Nutrition Constants ---
PROTEIN_PER_KG = 1.6   # g/kg, a good starting point for muscle synthesis
FAT_MIN_G_PER_KG = 0.6 # g/kg, essential for hormonal function

# --- Default Food Options ---
# These lists provide a baseline for the meal planner if the user doesn't specify preferences.
DEFAULT_FOODS = {
    "proteins": ["chicken breast", "eggs", "tuna", "lean beef", "laban protein", "tofu"],
    "carbs": ["rice", "oats", "potatoes", "pasta", "whole-wheat bread", "dates", "fruit"],
    "fats": ["olive oil", "avocado", "nuts", "tahini", "peanut butter"],
    "veggies": ["salad mix", "cucumber", "tomato", "broccoli", "spinach"],
}

# ==============================================================================
# SECTION 2: CORE HELPER FUNCTIONS
# (These functions perform the core calculations for fitness metrics.)
# ==============================================================================

def calculate_bmi(height_cm: float, weight_kg: float) -> float:
    """Calculates Body Mass Index (BMI)."""
    if height_cm <= 0 or weight_kg <= 0:
        return 0.0
    height_m = height_cm / 100.0
    return round(weight_kg / (height_m ** 2), 2)

def get_bmi_category(bmi: float) -> str:
    """Returns the BMI category string based on the BMI value."""
    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 calculate_bmr_mifflin(sex: Literal["male", "female"], age: int, height_cm: float, weight_kg: float) -> float:
    """Calculates Basal Metabolic Rate (BMR) using the Mifflin-St Jeor equation."""
    # The Mifflin-St Jeor equation is considered more accurate than the older Harris-Benedict formula.
    s_offset = 5 if sex == "male" else -161
    return (10 * weight_kg) + (6.25 * height_cm) - (5 * age) + s_offset

def calculate_tdee(sex: str, age: int, height_cm: float, weight_kg: float, activity: str) -> Tuple[int, int, int]:
    """Calculates Total Daily Energy Expenditure (TDEE) and target calories."""
    bmr = calculate_bmr_mifflin(sex, age, height_cm, weight_kg)
    factor = ACTIVITY_MULTIPLIERS.get(activity, 1.2)
    maintenance_calories = round(bmr * factor)
    # Gentle fat loss (-500 kcal) and lean bulk (+300 kcal) are common, sustainable targets.
    cut_calories = maintenance_calories - 500
    bulk_calories = maintenance_calories + 300
    return maintenance_calories, cut_calories, bulk_calories

def calculate_water_intake(weight_kg: float) -> int:
    """Estimates daily water intake based on body weight."""
    # A common guideline is 35 ml of water per kg of body weight.
    return int(weight_kg * 35)

def calculate_macros(weight_kg: float, calories: int) -> Tuple[int, int, int]:
    """Calculates macronutrient split (Protein, Carbs, Fat) for a given calorie target."""
    protein_g = int(round(PROTEIN_PER_KG * weight_kg))
    fat_g = int(round(FAT_MIN_G_PER_KG * weight_kg))
    calories_from_protein = protein_g * 4
    calories_from_fat = fat_g * 9
    remaining_calories = calories - calories_from_protein - calories_from_fat
    carbs_g = max(0, int(round(remaining_calories / 4)))
    return protein_g, carbs_g, fat_g



def filter_foods(options: List[str], avoid: List[str]) -> List[str]:
    """Filters a list of food options based on a list of items to avoid."""
    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

# --- Workout Split Definitions ---
# These define the workout routines for different goals and days.
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) -> List[str]:
    """Provides a compact, beginner-friendly template for a given workout session type."""
    # Equipment and level considerations can be added here for more dynamic 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)"]


# ==============================================================================
# SECTION 3: AGENT FUNCTIONS
# (These functions act as specialized "agents" providing fitness advice.)
# ==============================================================================

def agent_bmi_tdee(sex: str, age: int, height_cm: float, weight_kg: float, activity: str, goal: str) -> str:
    """Agent for calculating BMI, TDEE, and daily calorie targets."""
    bmi = calculate_bmi(height_cm, weight_kg)
    category = get_bmi_category(bmi)
    maintenance_cals, cut_cals, bulk_cals = calculate_tdee(sex, age, height_cm, weight_kg, activity)

    target_cals = {"Lose fat": cut_cals, "Maintain": maintenance_cals, "Build muscle": bulk_cals}[goal]
    water_ml = calculate_water_intake(weight_kg)
    water_l = round(water_ml / 1000, 1)

    coach_message = generate_coach_response("bmi_tdee", {
        "bmi": bmi, "category": category, "maintenance_cals": maintenance_cals,
        "target_cals": target_cals, "goal": goal, "water_l": water_l
    })
    return coach_message

def agent_workout(level: str, days_per_week: int, goal: str, equipment: str) -> str:
    """Agent for generating a weekly workout plan."""
    days_per_week = max(2, min(6, int(days_per_week))) # Ensure days are between 2 and 6
    base_split = GOAL_SPLITS[goal]
    workout_plan = []

    for i in range(days_per_week):
        session_name = base_split[i % len(base_split)]
        exercises = session_template(session_name)
        workout_plan.append((f"Day {i+1}{session_name}", exercises))

    coach_message = generate_coach_response("workout", {
        "workout_plan": workout_plan, "goal": goal, "level": level, "equipment": equipment
    })
    return coach_message

def agent_nutrition(weight_kg: float, target_kcal: int, liked_csv: str, avoid_csv: str, meals: int) -> str:
    """Agent for generating a nutrition plan and macronutrient breakdown."""
    liked_foods = [x.strip() for x in liked_csv.split(",") if x.strip()]
    avoid_foods = [x.strip() for x in avoid_csv.split(",") if x.strip()]

    # Filter default foods based on user preferences and avoidances
    proteins = filter_foods((liked_foods or DEFAULT_FOODS["proteins"]), avoid_foods)
    carbs    = filter_foods(DEFAULT_FOODS["carbs"], avoid_foods)
    fats     = filter_foods(DEFAULT_FOODS["fats"], avoid_foods)
    veggies  = filter_foods(DEFAULT_FOODS["veggies"], avoid_foods)

    protein_g, carb_g, fat_g = calculate_macros(weight_kg, target_kcal)
    meals_per_day = max(1, meals)
    protein_per_meal, carb_per_meal, fat_per_meal = protein_g // meals_per_day, carb_g // meals_per_day, fat_g // meals_per_day

    sample_day_meals = [
        f"Meal {i+1}: {proteins[i % len(proteins)]} + {carbs[i % len(carbs)]} "
        f"+ {veggies[i % len(veggies)]} + {fats[i % len(fats)]}"
        for i in range(meals_per_day)
    ]

    coach_message = generate_coach_response("nutrition", {
        "target_kcal": target_kcal, "protein_g": protein_g, "carb_g": carb_g, "fat_g": fat_g,
        "protein_per_meal": protein_per_meal, "carb_per_meal": carb_per_meal, "fat_per_meal": fat_per_meal,
        "sample_day_meals": sample_day_meals, "proteins": proteins, "carbs": carbs, "fats": fats, "veggies": veggies
    })
    return coach_message

def coach_all_in_one(
    sex, age, height_cm, weight_kg, activity, goal,
    level, days, equipment,
    liked_csv, avoid_csv, meals
) -> str:
    """Combines all agents to provide a comprehensive fitness and nutrition plan."""
    # BMI & TDEE Report
    bmi_tdee_report = agent_bmi_tdee(sex, age, height_cm, weight_kg, activity, goal)

    # Calculate target calories for nutrition agent
    _, cut_cals, bulk_cals = calculate_tdee(sex, age, height_cm, weight_kg, activity)
    target_kcal_for_nutrition = {"Lose fat": cut_cals, "Maintain": calculate_tdee(sex, age, height_cm, weight_kg, activity)[0], "Build muscle": bulk_cals}[goal]

    # Workout Plan
    workout_report = agent_workout(level, days, goal, equipment)

    # Nutrition Plan
    nutrition_report = agent_nutrition(weight_kg, target_kcal_for_nutrition, liked_csv, avoid_csv, meals)

    return f"{bmi_tdee_report}\n---\n{workout_report}\n---\n{nutrition_report}"


# ==============================================================================
# SECTION 4: AI COACH PERSONA - RESPONSE GENERATION
# (This function adds a conversational layer to the output.)
# ==============================================================================

def generate_coach_response(agent_type: str, data: dict) -> str:
    """Generates a coach-like, encouraging response based on agent output.
    This adds a 'persona' to the AI, making the interaction more engaging.
    """
    if agent_type == "bmi_tdee":
        bmi = data["bmi"]
        category = data["category"]
        maintenance_cals = data["maintenance_cals"]
        target_cals = data["target_cals"]
        goal = data["goal"]
        water_l = data["water_l"]

        response = f"""### 📊 Your Fitness Snapshot, Coach!
Hey there, future fitness legend! Let's break down your numbers:

- **BMI:** `{bmi}` → **{category}**
  *Coach says:* This gives us a baseline, but remember, it's just one piece of the puzzle! We focus on overall health.
- **Maintenance Calories:** **{maintenance_cals} kcal/day**
  *Coach says:* This is your daily energy sweet spot to stay exactly where you are.
- **Target for your goal ({goal}):** **{target_cals} kcal/day**
  *Coach says:* To hit your {goal.lower()} goal, we're aiming for this calorie target. Every bite counts towards your success!
- **Water recommendation:** ~ **{water_l} L/day**
  *Coach says:* Hydration is key for energy, recovery, and overall awesomeness! Keep that water bottle close.

Great start! Let's keep this momentum going!"""

    elif agent_type == "workout":
        workout_plan = data["workout_plan"]
        goal = data["goal"]
        level = data["level"]
        equipment = data["equipment"]

        plan_md = ""
        for title, items in workout_plan:
            plan_md += f"\n**{title}**\n" + "\n".join([f"- {x}" for x in items]) + "\n"

        response = f"""### 🏋️ Your Personalized Workout Plan, Champ!
Alright, {level} athlete! Here’s your custom workout roadmap to {goal.lower()}:
{plan_md}
*Coach says:* Remember to warm up, cool down, and listen to your body. Focus on form over weight, and let's crush those goals! Rest 60–90s between sets. Aim for ~2 reps in reserve (RIR 2).

Let's get those gains!"""

    elif agent_type == "nutrition":
        target_kcal = data["target_kcal"]
        protein_g = data["protein_g"]
        carb_g = data["carb_g"]
        fat_g = data["fat_g"]
        protein_per_meal = data["protein_per_meal"]
        carb_per_meal = data["carb_per_meal"]
        fat_per_meal = data["fat_per_meal"]
        sample_day_meals = data["sample_day_meals"]
        proteins = data["proteins"]
        carbs = data["carbs"]
        fats = data["fats"]
        veggies = data["veggies"]

        sample_meals_md = chr(10).join([f"- {m}" for m in sample_day_meals])

        response = f"""### 🥗 Fuel Your Success: Your Nutrition Blueprint!
Nutrition is your secret weapon, and here’s how we’re fueling your body for **{target_kcal} kcal/day**:

- **Macros (approx.):** Protein **{protein_g} g**, Carbs **{carb_g} g**, Fat **{fat_g} g**
  *Coach says:* This balance will support your energy and recovery. Per meal, that's roughly **{protein_per_meal}/{carb_per_meal}/{fat_per_meal} g** (P/C/F).
- **Preferred foods used:** {

", ".join(proteins[:3])}

**Sample Day:**
{sample_meals_md}

**Grocery list (starter):**
- **Protein:** {", ".join(proteins)}
- **Carbs:** {", ".join(carbs)}
- **Fats:** {", ".join(fats)}
- **Veggies:** {", ".join(veggies)}

*Coach says:* Aim for 20–30g protein per meal, load up on diverse veggies, and don't forget 1–2 pieces of fruit daily for those vital micronutrients! Let’s nourish that body!"""

    else:
        response = """### 🤔 Unrecognized Request
My apologies, coach. I'm not sure how to respond to that request yet. Let's stick to the plan!"""

    return response


# ==============================================================================
# SECTION 5: GRADIO USER INTERFACE
# (Defines the interactive web interface using Gradio.)
# ==============================================================================

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;
      }
      h3 { font-size: 1.5em; margin-top: 1em; }
      h4 { font-size: 1.2em; margin-top: 0.8em; }
      strong { color: #2E8B57; } /* A nice green for emphasis */
      /* Further styling can be added here for a more polished look */
    """
) as demo:
    gr.Markdown("## 🏋️ Beginner Gym Coach — Multi-Agent\n### Your AI-Powered Guide to a Healthier You!\nGive me your basics and I’ll coach you to success.")
    gr.HTML(MOTIVATION_JS)

    with gr.Tabs():
        # -------- BMI & TDEE Tab --------
        with gr.Tab("BMI & TDEE"):
            gr.Markdown("#### Understand your body composition and daily energy needs.")
            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_MULTIPLIERS.keys()),
                                           value="Light (1-3x/wk)", label="Activity Level")
                    goal = gr.Radio(["Lose fat","Maintain","Build muscle"],
                                    value="Maintain", label="Fitness Goal")
                    btn1 = gr.Button("Calculate BMI & TDEE")
                with gr.Column(scale=1):
                    out1 = gr.Markdown()
            btn1.click(agent_bmi_tdee, [sex, age, height, weight, activity, goal], out1)

        # -------- Workout Plan Tab --------
        with gr.Tab("Workout Plan"):
            gr.Markdown("#### Get a structured weekly workout plan tailored to your goals.")
            with gr.Row():
                with gr.Column(scale=1):
                    level = gr.Radio(["Beginner","Intermediate"], value="Beginner", label="Experience Level")
                    days = gr.Slider(2, 6, value=4, step=1, label="Training Days per Week")
                    goal_w = gr.Radio(["Lose fat","Maintain","Build muscle"],
                                      value="Build muscle", label="Workout Goal")
                    equipment = gr.Dropdown(["Gym (machines/dumbbells)",
                                             "Home (bands/db)", "Bodyweight only"],
                                            value="Gym (machines/dumbbells)", label="Available Equipment")
                    btn2 = gr.Button("Generate Workout Plan")
                with gr.Column(scale=1):
                    out2 = gr.Markdown()
            btn2.click(agent_workout, [level, days, goal_w, equipment], out2)

        # -------- Nutrition Tab --------
        with gr.Tab("Nutrition"):
            gr.Markdown("#### Discover your macro breakdown and a sample meal plan.")
            with gr.Row():
                with gr.Column(scale=1):
                    target_kcal = gr.Number(value=1900, label="Target Calories (kcal/day)")
                    weight_n = gr.Number(value=60, label="Current Weight (kg)")
                    liked = gr.Textbox(label="Preferred foods (comma-separated)",
                                       placeholder="e.g., chicken, eggs, rice, oats, salad, laban")
                    avoid = gr.Textbox(label="Foods to avoid (allergies, dislikes - comma-separated)",
                                       placeholder="e.g., nuts, shrimp, gluten")
                    meals = gr.Slider(2, 6, value=3, step=1, label="Meals per 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 Coach Tab --------
        with gr.Tab("All-in-One Coach"):
            gr.Markdown("#### Get a complete, integrated fitness and nutrition strategy.")
            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_MULTIPLIERS.keys()),
                                            value="Light (1-3x/wk)", label="Activity Level")
                    goal2 = gr.Radio(["Lose fat","Maintain","Build muscle"],
                                     value="Build muscle", label="Overall Goal")
                    level2 = gr.Radio(["Beginner","Intermediate"], value="Beginner", label="Experience Level")
                    days2 = gr.Slider(2, 6, value=4, step=1, label="Training Days per Week")
                    equipment2 = gr.Dropdown(["Gym (machines/dumbbells)",
                                              "Home (bands/db)", "Bodyweight only"],
                                             value="Gym (machines/dumbbells)", label="Available Equipment")
                    liked2 = gr.Textbox(label="Preferred foods", placeholder="e.g., chicken, eggs, rice, potatoes")
                    avoid2 = gr.Textbox(label="Foods to avoid", placeholder="e.g., nuts, shrimp")
                    meals2 = gr.Slider(2, 6, value=3, step=1, label="Meals per 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()