Spaces:
Running
Running
| import os | |
| import json | |
| import gradio as gr | |
| from openai import OpenAI | |
| from dotenv import load_dotenv | |
| # Load environment variables from .env file | |
| load_dotenv() | |
| # Configure the OpenAI API key | |
| api_key = os.environ.get("OPENAI_API_KEY") | |
| if api_key: | |
| client = OpenAI(api_key=api_key) | |
| else: | |
| print("Warning: OPENAI_API_KEY not found in environment variables.") | |
| client = None | |
| # Create a prompt template for the diet plan | |
| def create_prompt_template(user_data): | |
| # Get the number of meals per day | |
| meals_per_day = user_data.get('meals_per_day', 3) | |
| # Define the meal structure based on the number of meals | |
| meal_structure = [] | |
| if meals_per_day == 3: | |
| meal_structure = ["breakfast", "lunch", "dinner"] | |
| elif meals_per_day == 4: | |
| meal_structure = ["breakfast", "lunch", "evening snack", "dinner"] | |
| elif meals_per_day == 5: | |
| meal_structure = ["breakfast", "morning snack", "lunch", "evening snack", "dinner"] | |
| elif meals_per_day == 6: | |
| meal_structure = ["breakfast", "mid-morning snack", "lunch", "afternoon snack", "evening snack", "dinner"] | |
| elif meals_per_day == 7: | |
| meal_structure = ["early morning", "breakfast", "mid-morning snack", "lunch", "afternoon snack", "evening snack", "dinner"] | |
| meals_description = ", ".join(meal_structure) | |
| prompt = f""" | |
| You are an expert nutritionist and dietitian. Create a personalized 7-day diet plan based on the following user information: | |
| USER PROFILE: | |
| - Age: {user_data.get('age')} | |
| - Gender: {user_data.get('gender')} | |
| - Weight: {user_data.get('current_weight')} kg | |
| - Height: {user_data.get('height')} cm | |
| - BMI: {user_data.get('bmi')} | |
| - Basal Metabolic Rate (BMR): {user_data.get('bmr')} calories | |
| - Daily Activity Level: {user_data.get('activity_level')} | |
| - Goal: {user_data.get('goal')} ({user_data.get('target_weight')} kg) | |
| - Dietary Preferences/Restrictions: {user_data.get('dietary_preferences', 'None')} | |
| - Health Conditions: {user_data.get('health_conditions', 'None')} | |
| - Food Allergies: {user_data.get('food_allergies', 'None')} | |
| - Calorie Target Range: {user_data.get('calorie_range')} | |
| - Meals Per Day: {user_data.get('meals_per_day')} ({meals_description}) | |
| REQUIREMENTS: | |
| 1. Create a detailed, personalized 7-day diet plan with {meals_per_day} meals per day ({meals_description}) | |
| 2. Include specific food items with approximate quantities in grams or standard measurements | |
| 3. Calculate and display the approximate calorie count for each meal | |
| 4. Include a mix of proteins, carbohydrates, and fats in appropriate proportions | |
| 5. Consider the user's dietary preferences, restrictions, and allergies | |
| 6. Provide a daily macronutrient breakdown (protein, carbs, fats) | |
| 7. Add brief nutritional notes explaining why certain foods are recommended | |
| 8. Include 2-3 general nutrition tips customized to the user's goals | |
| 9. Format the response in a clean, organized way that's easy to read | |
| RESPONSE FORMAT: | |
| - Start with a personalized introduction that summarizes the plan and its goals | |
| - Organize each day clearly with all {meals_per_day} meals | |
| - Include a macronutrient summary at the end of each day | |
| - End with general tips and recommendation for water intake | |
| - Keep the tone supportive and encouraging | |
| Structure your response in JSON format using the following schema: | |
| ``` | |
| {{ | |
| "introduction": "string", | |
| "daily_plans": [ | |
| {{ | |
| "day": "Day 1", | |
| "meals": {{ | |
| """ | |
| # Dynamically add the meal structure to the JSON schema based on the number of meals | |
| for meal in meal_structure: | |
| meal_key = meal.lower().replace(" ", "_") | |
| prompt += f' "{meal_key}": {{"foods": ["item with quantity"], "calories": number}},\n' | |
| # Remove the last comma if there is one | |
| prompt = prompt.rstrip(',\n') + '\n' | |
| prompt += f""" }}, | |
| "total_calories": number, | |
| "macros": {{"protein": "x g", "carbs": "y g", "fats": "z g"}} | |
| }}, | |
| // Days 2-7 follow same structure | |
| ], | |
| "nutrition_tips": ["tip 1", "tip 2", "tip 3"], | |
| "water_recommendation": "string" | |
| }} | |
| ``` | |
| Only return the JSON response with no additional text. | |
| """ | |
| return prompt | |
| # Calculate BMI | |
| def calculate_bmi(weight, height): | |
| """Calculate BMI from weight in kg and height in cm""" | |
| height_in_meters = height / 100 | |
| bmi = weight / (height_in_meters * height_in_meters) | |
| return round(bmi, 2) | |
| # Calculate BMR using Mifflin-St Jeor Equation | |
| def calculate_bmr(weight, height, age, gender): | |
| """Calculate BMR using Mifflin-St Jeor Equation""" | |
| if gender.lower() == 'male': | |
| bmr = (10 * weight) + (6.25 * height) - (5 * age) + 5 | |
| else: | |
| bmr = (10 * weight) + (6.25 * height) - (5 * age) - 161 | |
| return round(bmr) | |
| # Calculate Daily Calorie Needs | |
| def calculate_daily_calories(bmr, activity_level): | |
| """Calculate daily calorie needs based on BMR and activity level""" | |
| activity_multipliers = { | |
| 'sedentary': 1.2, # Little or no exercise | |
| 'light': 1.375, # Light exercise 1-3 days per week | |
| 'moderate': 1.55, # Moderate exercise 3-5 days per week | |
| 'active': 1.725, # Hard exercise 6-7 days per week | |
| 'very_active': 1.9 # Very hard exercise & physical job or training twice a day | |
| } | |
| multiplier = activity_multipliers.get(activity_level.lower(), 1.2) | |
| return round(bmr * multiplier) | |
| # Calculate Calorie Target for Weight Loss/Gain | |
| def calculate_calorie_target(maintenance_calories, goal, rate=0.5): | |
| """ | |
| Calculate calorie target for weight loss or gain | |
| rate: Rate of weight change in kg/week (default 0.5 kg) | |
| """ | |
| # 1 kg of fat is approximately 7700 calories | |
| daily_calorie_adjustment = (7700 * rate) / 7 | |
| if goal.lower() == 'weight loss': | |
| return round(maintenance_calories - daily_calorie_adjustment) | |
| elif goal.lower() == 'weight gain': | |
| return round(maintenance_calories + daily_calorie_adjustment) | |
| else: # maintenance | |
| return maintenance_calories | |
| # Main function to generate diet plan | |
| def generate_diet_plan( | |
| age, gender, current_weight, height, activity_level, goal, | |
| target_weight, dietary_preferences, health_conditions, food_allergies, meals_per_day | |
| ): | |
| try: | |
| # Convert inputs to appropriate types | |
| age = int(age) | |
| current_weight = float(current_weight) | |
| height = float(height) | |
| target_weight = float(target_weight) if target_weight else None | |
| meals_per_day = int(meals_per_day) | |
| # Validate inputs | |
| if age <= 0 or current_weight <= 0 or height <= 0: | |
| return "Please enter valid values for age, weight, and height." | |
| if meals_per_day < 3 or meals_per_day > 7: | |
| return "Please select between 3 and 7 meals per day." | |
| # Create user data dictionary | |
| user_data = { | |
| 'age': age, | |
| 'gender': gender, | |
| 'current_weight': current_weight, | |
| 'height': height, | |
| 'activity_level': activity_level, | |
| 'goal': goal, | |
| 'target_weight': target_weight, | |
| 'dietary_preferences': dietary_preferences or 'None', | |
| 'health_conditions': health_conditions or 'None', | |
| 'food_allergies': food_allergies or 'None', | |
| 'meals_per_day': meals_per_day | |
| } | |
| # Calculate BMI | |
| user_data['bmi'] = calculate_bmi(current_weight, height) | |
| # Calculate BMR | |
| user_data['bmr'] = calculate_bmr(current_weight, height, age, gender) | |
| # Calculate maintenance calories | |
| maintenance_calories = calculate_daily_calories(user_data['bmr'], activity_level) | |
| # Calculate target calories based on goal | |
| target_calories = calculate_calorie_target(maintenance_calories, goal) | |
| buffer = 100 # Buffer range for flexibility | |
| user_data['calorie_range'] = f"{target_calories - buffer} - {target_calories + buffer}" | |
| # Generate the prompt from the template | |
| prompt = create_prompt_template(user_data) | |
| # Generate diet plan using OpenAI | |
| response = client.chat.completions.create( | |
| model="gpt-4o", | |
| messages=[ | |
| { | |
| "role": "user", | |
| "content": prompt | |
| } | |
| ], | |
| max_tokens=4000, | |
| temperature=0.7 | |
| ) | |
| response_text = response.choices[0].message.content | |
| # Parse the response to extract the JSON | |
| try: | |
| # Attempt to parse the response directly | |
| diet_plan = json.loads(response_text) | |
| except json.JSONDecodeError: | |
| # If parsing fails, try to extract JSON from markdown code blocks | |
| import re | |
| json_match = re.search(r'```(?:json)?(.*?)```', response_text, re.DOTALL) | |
| if json_match: | |
| diet_plan = json.loads(json_match.group(1).strip()) | |
| else: | |
| # Last resort: clean up the text and try to parse | |
| cleaned_text = response_text.replace('```json', '').replace('```', '') | |
| try: | |
| diet_plan = json.loads(cleaned_text.strip()) | |
| except json.JSONDecodeError: | |
| return {"error": f"Failed to parse the response from the AI model."} | |
| # Add user profile data to the response for context | |
| diet_plan["user_profile"] = { | |
| "age": age, | |
| "gender": gender, | |
| "current_weight": current_weight, | |
| "height": height, | |
| "bmi": user_data['bmi'], | |
| "bmr": user_data['bmr'], | |
| "activity_level": activity_level, | |
| "goal": goal, | |
| "target_weight": target_weight, | |
| "dietary_preferences": dietary_preferences or "None", | |
| "health_conditions": health_conditions or "None", | |
| "food_allergies": food_allergies or "None", | |
| "meals_per_day": meals_per_day, | |
| "calorie_range": user_data['calorie_range'] | |
| } | |
| # For UI display in Gradio, format as Markdown | |
| formatted_plan = format_diet_plan_markdown(diet_plan) | |
| # Return the diet plan - different return formats for different functions | |
| return formatted_plan, diet_plan | |
| except Exception as e: | |
| return f"An error occurred: {str(e)}", {"error": f"An error occurred: {str(e)}"} | |
| # Format the diet plan as Markdown for better display in Gradio | |
| def format_diet_plan_markdown(diet_plan): | |
| markdown = f"# Your Personalized Diet Plan\n\n" | |
| markdown += f"## Introduction\n{diet_plan['introduction']}\n\n" | |
| # Format each day | |
| for day_plan in diet_plan['daily_plans']: | |
| markdown += f"## {day_plan['day']}\n\n" | |
| # Format each meal | |
| for meal_type, meal_data in day_plan['meals'].items(): | |
| # Format meal name | |
| meal_name = meal_type.replace('_', ' ').title() | |
| markdown += f"### {meal_name} ({meal_data['calories']} calories)\n" | |
| # List foods | |
| for food in meal_data['foods']: | |
| markdown += f"- {food}\n" | |
| markdown += "\n" | |
| # Daily summary | |
| markdown += f"**Daily Total:** {day_plan['total_calories']} calories\n\n" | |
| markdown += f"**Macros:** Protein: {day_plan['macros']['protein']} | Carbs: {day_plan['macros']['carbs']} | Fats: {day_plan['macros']['fats']}\n\n" | |
| markdown += "---\n\n" | |
| # Nutrition tips | |
| markdown += "## Nutrition Tips\n\n" | |
| for tip in diet_plan['nutrition_tips']: | |
| markdown += f"- {tip}\n" | |
| markdown += "\n" | |
| # Water recommendation | |
| markdown += f"## Water Recommendation\n{diet_plan['water_recommendation']}\n" | |
| return markdown | |
| # Define Gradio interface | |
| def create_gradio_interface(): | |
| with gr.Blocks(title="Personalized Diet Plan Generator") as app: | |
| gr.Markdown("# Personalized Diet Plan Generator") | |
| gr.Markdown("Enter your information below to generate a customized diet plan.") | |
| with gr.Row(): | |
| with gr.Column(): | |
| # Basic information | |
| age = gr.Number(label="Age", minimum=1, maximum=120, step=1, value=30) | |
| gender = gr.Radio( | |
| choices=["male", "female", "other"], | |
| label="Gender", | |
| value="male" | |
| ) | |
| current_weight = gr.Number(label="Current Weight (kg)", minimum=30, maximum=300, step=0.1, value=70) | |
| height = gr.Number(label="Height (cm)", minimum=100, maximum=250, step=0.1, value=170) | |
| # Activity level | |
| activity_level = gr.Dropdown( | |
| choices=["sedentary", "light", "moderate", "active", "very_active"], | |
| label="Activity Level", | |
| value="moderate", | |
| info="sedentary (little exercise), light (1-3 days/week), moderate (3-5 days/week), active (6-7 days/week), very_active (hard exercise daily)" | |
| ) | |
| # Goal and target weight | |
| goal = gr.Radio( | |
| choices=["weight loss", "weight gain", "maintenance"], | |
| label="Goal", | |
| value="weight loss" | |
| ) | |
| target_weight = gr.Number(label="Target Weight (kg)", minimum=30, maximum=300, step=0.1) | |
| with gr.Column(): | |
| # Dietary preferences and restrictions | |
| dietary_preferences = gr.Textbox( | |
| label="Dietary Preferences", | |
| placeholder="e.g., vegetarian, vegan, keto, Mediterranean", | |
| lines=2 | |
| ) | |
| health_conditions = gr.Textbox( | |
| label="Health Conditions", | |
| placeholder="e.g., diabetes, hypertension, thyroid issues", | |
| lines=2 | |
| ) | |
| food_allergies = gr.Textbox( | |
| label="Food Allergies", | |
| placeholder="e.g., nuts, dairy, gluten, seafood", | |
| lines=2 | |
| ) | |
| # Meals per day | |
| meals_per_day = gr.Slider( | |
| minimum=3, | |
| maximum=7, | |
| step=1, | |
| value=3, | |
| label="Meals Per Day" | |
| ) | |
| # Generate button | |
| generate_button = gr.Button("Generate Diet Plan", variant="primary") | |
| # Output tabs for both Markdown and JSON views | |
| with gr.Tabs(): | |
| with gr.TabItem("Formatted Plan"): | |
| output_markdown = gr.Markdown(label="Generated Diet Plan") | |
| with gr.TabItem("JSON Data"): | |
| output_json = gr.JSON(label="Diet Plan JSON") | |
| # Set up the function to trigger when the button is clicked | |
| generate_button.click( | |
| fn=generate_diet_plan, | |
| inputs=[ | |
| age, gender, current_weight, height, activity_level, goal, | |
| target_weight, dietary_preferences, health_conditions, food_allergies, meals_per_day | |
| ], | |
| outputs=[output_markdown, output_json] | |
| ) | |
| # Examples | |
| examples = [ | |
| [30, "female", 65, 165, "moderate", "weight loss", 60, "vegetarian", "none", "lactose intolerance", 3], | |
| [40, "male", 85, 180, "active", "weight gain", 90, "high-protein", "none", "none", 5], | |
| [25, "female", 58, 160, "light", "maintenance", 58, "vegan", "none", "nuts", 4] | |
| ] | |
| gr.Examples( | |
| examples=examples, | |
| inputs=[ | |
| age, gender, current_weight, height, activity_level, goal, | |
| target_weight, dietary_preferences, health_conditions, food_allergies, meals_per_day | |
| ] | |
| ) | |
| # API documentation | |
| gr.Markdown(""" | |
| ## API Usage | |
| This diet plan generator is also available as an API. You can make POST requests to `/api/predict` with the following JSON structure: | |
| ```json | |
| { | |
| "data": [30, "female", 65, 165, "moderate", "weight loss", 60, "vegetarian", "none", "lactose intolerance", 3] | |
| } | |
| ``` | |
| Parameters order: | |
| 1. age (number) | |
| 2. gender (string: "male", "female", or "other") | |
| 3. current_weight (number in kg) | |
| 4. height (number in cm) | |
| 5. activity_level (string: "sedentary", "light", "moderate", "active", "very_active") | |
| 6. goal (string: "weight loss", "weight gain", "maintenance") | |
| 7. target_weight (number in kg) | |
| 8. dietary_preferences (string) | |
| 9. health_conditions (string) | |
| 10. food_allergies (string) | |
| 11. meals_per_day (number: 3-7) | |
| The response will contain the complete diet plan in JSON format, ready for integration into your website. | |
| """) | |
| return app | |
| # Create the main interface | |
| iface = create_gradio_interface() | |
| # Launch the app with correct API config | |
| if __name__ == "__main__": | |
| iface.launch(share=True) |