Spaces:
Runtime error
Runtime error
| import json | |
| from config.settings import client, MODEL | |
| class NutritionAdvisor: | |
| def __init__(self): | |
| self.rules = self._load_rules() | |
| def _load_rules(self): | |
| """Load nutrition rules from rules.json""" | |
| try: | |
| with open('modules/rules.json', 'r', encoding='utf-8') as f: | |
| return json.load(f).get('nutrition', {}) | |
| except: | |
| return self._get_default_rules() | |
| def _get_default_rules(self): | |
| """Default nutrition rules if file not found""" | |
| return { | |
| "bmi_categories": { | |
| "underweight": {"min": 0, "max": 18.5, "advice": "Tăng cân lành mạnh"}, | |
| "normal": {"min": 18.5, "max": 24.9, "advice": "Duy trì cân nặng"}, | |
| "overweight": {"min": 25, "max": 29.9, "advice": "Giảm cân nhẹ"}, | |
| "obese": {"min": 30, "max": 100, "advice": "Giảm cân cần thiết"} | |
| }, | |
| "age_groups": { | |
| "child": {"min": 0, "max": 12, "focus": "Phát triển"}, | |
| "teen": {"min": 13, "max": 19, "focus": "Tăng trưởng"}, | |
| "adult": {"min": 20, "max": 59, "focus": "Duy trì"}, | |
| "elderly": {"min": 60, "max": 120, "focus": "Sức khỏe"} | |
| }, | |
| "goals": { | |
| "weight_loss": "Giảm cân", | |
| "weight_gain": "Tăng cân", | |
| "muscle_building": "Xây dựng cơ bắp", | |
| "maintenance": "Duy trì", | |
| "health_improvement": "Cải thiện sức khỏe" | |
| }, | |
| "gender_nutrition": { | |
| "male": {"calorie_base": 2500, "protein_ratio": 0.25}, | |
| "female": {"calorie_base": 2000, "protein_ratio": 0.22} | |
| } | |
| } | |
| def calculate_bmi(self, weight, height): | |
| """Calculate BMI with validation""" | |
| # Validate inputs | |
| if not weight or not height: | |
| return 0 | |
| # Height should be in cm (50-300 range) | |
| if height < 50 or height > 300: | |
| # Likely wrong unit or extraction error | |
| return 0 | |
| # Weight should be in kg (20-300 range) | |
| if weight < 20 or weight > 300: | |
| return 0 | |
| height_m = height / 100 | |
| bmi = weight / (height_m ** 2) | |
| # BMI should be reasonable (10-60 range) | |
| if bmi < 10 or bmi > 60: | |
| return 0 | |
| return round(bmi, 1) | |
| def get_bmi_category(self, bmi): | |
| """Get BMI category and advice""" | |
| for category, data in self.rules["bmi_categories"].items(): | |
| if data["min"] <= bmi < data["max"]: | |
| return { | |
| "category": category, | |
| "advice": data["advice"], | |
| "bmi": bmi | |
| } | |
| return {"category": "unknown", "advice": "Cần đánh giá thêm", "bmi": bmi} | |
| def get_age_group(self, age): | |
| """Get age group and focus""" | |
| for group, data in self.rules["age_groups"].items(): | |
| if data["min"] <= age <= data["max"]: | |
| return {"group": group, "focus": data["focus"]} | |
| return {"group": "unknown", "focus": "Sức khỏe tổng quát"} | |
| def generate_nutrition_advice(self, user_data): | |
| """ | |
| Generate personalized nutrition advice | |
| Args: | |
| user_data (dict): { | |
| 'age': int, | |
| 'gender': str ('male'/'female'), | |
| 'weight': float (kg), | |
| 'height': float (cm), | |
| 'goal': str, | |
| 'activity_level': str ('low'/'moderate'/'high'), | |
| 'dietary_restrictions': list, | |
| 'health_conditions': list | |
| } | |
| Returns: | |
| dict: Comprehensive nutrition advice | |
| """ | |
| # Calculate BMI and get category | |
| bmi = self.calculate_bmi(user_data['weight'], user_data['height']) | |
| bmi_info = self.get_bmi_category(bmi) | |
| # Get age group | |
| age_info = self.get_age_group(user_data['age']) | |
| # Get gender-specific nutrition base | |
| gender_info = self.rules["gender_nutrition"].get(user_data['gender'], | |
| self.rules["gender_nutrition"]["female"]) | |
| # Prepare context for LLM | |
| nutrition_context = self._build_nutrition_context(user_data, bmi_info, age_info, gender_info) | |
| # Generate LLM advice | |
| llm_advice = self._get_llm_nutrition_advice(nutrition_context) | |
| # Calculate daily nutrition targets | |
| daily_targets = self._calculate_daily_targets(user_data, gender_info, bmi_info) | |
| return { | |
| "bmi_analysis": bmi_info, | |
| "age_group": age_info, | |
| "daily_targets": daily_targets, | |
| "personalized_advice": llm_advice, | |
| "meal_suggestions": self._generate_meal_suggestions(user_data, bmi_info), | |
| "supplement_recommendations": self._get_supplement_recommendations(user_data, bmi_info) | |
| } | |
| def _build_nutrition_context(self, user_data, bmi_info, age_info, gender_info): | |
| """Build comprehensive context for LLM""" | |
| context = f""" | |
| Thông tin người dùng: | |
| - Tuổi: {user_data['age']} ({age_info['group']} - {age_info['focus']}) | |
| - Giới tính: {user_data['gender']} | |
| - Cân nặng: {user_data['weight']}kg, Chiều cao: {user_data['height']}cm | |
| - BMI: {bmi_info['bmi']} ({bmi_info['category']} - {bmi_info['advice']}) | |
| - Mục tiêu: {user_data['goal']} | |
| - Mức độ hoạt động: {user_data.get('activity_level', 'moderate')} | |
| Hạn chế ăn uống: {', '.join(user_data.get('dietary_restrictions', []))} | |
| Tình trạng sức khỏe: {', '.join(user_data.get('health_conditions', []))} | |
| Hãy đưa ra lời khuyên dinh dưỡng cụ thể, bao gồm: | |
| 1. Phân tích tình trạng hiện tại | |
| 2. Khuyến nghị calo hàng ngày | |
| 3. Tỷ lệ macro (protein/carb/fat) | |
| 4. Thực phẩm nên ăn và tránh | |
| 5. Lịch ăn uống phù hợp | |
| 6. Lời khuyên đặc biệt cho tình trạng sức khỏe | |
| """ | |
| return context | |
| def _get_llm_nutrition_advice(self, context): | |
| """Get personalized nutrition advice from LLM""" | |
| try: | |
| response = client.chat.completions.create( | |
| model=MODEL, | |
| messages=[ | |
| { | |
| "role": "system", | |
| "content": """Bạn là chuyên gia dinh dưỡng có kinh nghiệm. | |
| Đưa ra lời khuyên dinh dưỡng chính xác, khoa học và phù hợp với từng cá nhân. | |
| Luôn cân nhắc về tình trạng sức khỏe và hạn chế ăn uống. | |
| Trả lời bằng tiếng Việt, chi tiết và dễ hiểu.""" | |
| }, | |
| {"role": "user", "content": context} | |
| ], | |
| temperature=0.7, | |
| max_tokens=2000 | |
| ) | |
| return response.choices[0].message.content | |
| except Exception as e: | |
| return f"Không thể tạo lời khuyên dinh dưỡng: {str(e)}" | |
| def _calculate_daily_targets(self, user_data, gender_info, bmi_info): | |
| """Calculate daily nutrition targets""" | |
| base_calories = gender_info["calorie_base"] | |
| # Adjust based on goal | |
| goal_multipliers = { | |
| "weight_loss": 0.8, | |
| "weight_gain": 1.2, | |
| "muscle_building": 1.1, | |
| "maintenance": 1.0, | |
| "health_improvement": 1.0 | |
| } | |
| activity_multipliers = { | |
| "low": 1.2, | |
| "moderate": 1.4, | |
| "high": 1.6 | |
| } | |
| activity_level = user_data.get('activity_level', 'moderate') | |
| goal = user_data.get('goal', 'maintenance') | |
| daily_calories = int(base_calories * | |
| goal_multipliers.get(goal, 1.0) * | |
| activity_multipliers.get(activity_level, 1.4)) | |
| protein_ratio = gender_info["protein_ratio"] | |
| protein_calories = daily_calories * protein_ratio | |
| protein_grams = int(protein_calories / 4) | |
| fat_calories = daily_calories * 0.25 # 25% fat | |
| fat_grams = int(fat_calories / 9) | |
| carb_calories = daily_calories - protein_calories - fat_calories | |
| carb_grams = int(carb_calories / 4) | |
| return { | |
| "daily_calories": daily_calories, | |
| "protein": f"{protein_grams}g", | |
| "carbs": f"{carb_grams}g", | |
| "fats": f"{fat_grams}g", | |
| "water": "2.5-3.5L" | |
| } | |
| def _generate_meal_suggestions(self, user_data, bmi_info): | |
| """Generate meal suggestions based on user profile""" | |
| suggestions = { | |
| "breakfast": [ | |
| "Bánh mì nguyên cám + trứng + sữa", | |
| "Cháo yến mạch + trái cây", | |
| "Sinh tố protein + hạt chia" | |
| ], | |
| "lunch": [ | |
| "Cơm gạo lứt + thịt/cá + rau xanh", | |
| "Salad + ức gà + dầu olive", | |
| "Bún phở + thịt nạc + rau" | |
| ], | |
| "dinner": [ | |
| "Cá hồi + khoai lang + rau củ", | |
| "Thịt bò + cơm + canh rau", | |
| "Đậu phụ + rau xào + cơm" | |
| ], | |
| "snacks": [ | |
| "Hạt hạnh nhân + trái cây", | |
| "Sữa chua Hy Lạp + mật ong", | |
| "Trái cây + phô mai ít béo" | |
| ] | |
| } | |
| # Adjust based on BMI category | |
| if bmi_info["category"] == "underweight": | |
| suggestions["snacks"].extend(["Bơ đậu phộng + bánh mì", "Sinh tố tăng cân"]) | |
| elif bmi_info["category"] in ["overweight", "obese"]: | |
| suggestions["snacks"] = ["Trái cây ít ngọt", "Rau củ sống", "Trà xanh"] | |
| return suggestions | |
| def _get_supplement_recommendations(self, user_data, bmi_info): | |
| """Get supplement recommendations""" | |
| supplements = [] | |
| # Age-based recommendations | |
| if user_data['age'] > 50: | |
| supplements.extend(["Vitamin D3", "Calcium", "B12"]) | |
| # Gender-based | |
| if user_data['gender'] == 'female': | |
| supplements.extend(["Iron", "Folate"]) | |
| # Goal-based | |
| if user_data.get('goal') == 'muscle_building': | |
| supplements.extend(["Whey Protein", "Creatine"]) | |
| # BMI-based | |
| if bmi_info["category"] in ["overweight", "obese"]: | |
| supplements.extend(["Omega-3", "Probiotics"]) | |
| return list(set(supplements)) # Remove duplicates | |
| # Convenience function for external use | |
| def generate_nutrition_advice(user_data): | |
| """ | |
| Main function to generate nutrition advice | |
| Args: | |
| user_data (dict): User information including age, gender, weight, height, goal, etc. | |
| Returns: | |
| dict: Comprehensive nutrition advice and meal suggestions | |
| """ | |
| advisor = NutritionAdvisor() | |
| return advisor.generate_nutrition_advice(user_data) | |