rishi002's picture
fetch api as secret
fe4e6cc verified
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)