Spaces:
Running on Zero
Running on Zero
| """Per-serving macro estimator — ingredient lookup, no extra model call needed.""" | |
| from __future__ import annotations | |
| # (calories kcal, protein g, carbs g, fat g, fiber g) per 100 g | |
| _MACROS: dict[str, tuple[float, float, float, float, float]] = { | |
| # proteins | |
| "chicken": (165, 31, 0, 3.6, 0), | |
| "beef": (250, 26, 0, 16, 0), | |
| "pork": (242, 27, 0, 14, 0), | |
| "fish": (130, 20, 0, 5, 0), | |
| "salmon": (208, 20, 0, 13, 0), | |
| "tuna": (130, 29, 0, 0.5, 0), | |
| "shrimp": (99, 24, 0, 0.3, 0), | |
| "egg": (155, 13, 1.1, 11, 0), | |
| "eggs": (155, 13, 1.1, 11, 0), | |
| "tofu": (76, 8, 1.9, 4.8, 0.3), | |
| # dairy | |
| "milk": (61, 3.2, 4.8, 3.3, 0), | |
| "cheese": (402, 25, 1.3, 33, 0), | |
| "butter": (717, 0.9, 0.1, 81, 0), | |
| "yogurt": (59, 3.5, 4.7, 3.3, 0), | |
| "cream": (340, 2.1, 2.8, 36, 0), | |
| # starches | |
| "rice": (130, 2.7, 28, 0.3, 0.4), | |
| "pasta": (158, 5.8, 31, 0.9, 1.8), | |
| "bread": (265, 9, 49, 3.2, 2.7), | |
| "potato": (77, 2, 17, 0.1, 2.2), | |
| "potatoes": (77, 2, 17, 0.1, 2.2), | |
| "flour": (364, 10, 76, 1, 2.7), | |
| "oats": (389, 17, 66, 7, 10.6), | |
| "quinoa": (120, 4.1, 21, 1.9, 2.8), | |
| "lentils": (116, 9, 20, 0.4, 7.9), | |
| "beans": (347, 21, 60, 1.2, 15), | |
| "chickpeas": (164, 8.9, 27, 2.6, 7.6), | |
| # vegetables | |
| "tomato": (18, 0.9, 3.9, 0.2, 1.2), | |
| "tomatoes": (18, 0.9, 3.9, 0.2, 1.2), | |
| "onion": (40, 1.1, 9.3, 0.1, 1.7), | |
| "onions": (40, 1.1, 9.3, 0.1, 1.7), | |
| "garlic": (149, 6.4, 33, 0.5, 2.1), | |
| "carrot": (41, 0.9, 10, 0.2, 2.8), | |
| "carrots": (41, 0.9, 10, 0.2, 2.8), | |
| "broccoli": (34, 2.8, 7, 0.4, 2.6), | |
| "spinach": (23, 2.9, 3.6, 0.4, 2.2), | |
| "pepper": (31, 1, 6, 0.3, 2.1), | |
| "peppers": (31, 1, 6, 0.3, 2.1), | |
| "mushroom": (22, 3.1, 3.3, 0.3, 1), | |
| "mushrooms": (22, 3.1, 3.3, 0.3, 1), | |
| "zucchini": (17, 1.2, 3.1, 0.3, 1), | |
| "corn": (86, 3.3, 19, 1.4, 2.7), | |
| "lettuce": (15, 1.4, 2.9, 0.2, 1.3), | |
| "cucumber": (16, 0.7, 3.6, 0.1, 0.5), | |
| "eggplant": (25, 1, 5.9, 0.2, 3), | |
| "cabbage": (25, 1.3, 5.8, 0.1, 2.5), | |
| "celery": (16, 0.7, 3, 0.2, 1.6), | |
| "leek": (61, 1.5, 14, 0.3, 1.8), | |
| # fruits | |
| "apple": (52, 0.3, 14, 0.2, 2.4), | |
| "banana": (89, 1.1, 23, 0.3, 2.6), | |
| "lemon": (29, 1.1, 9.3, 0.3, 2.8), | |
| "lime": (30, 0.7, 10.5, 0.2, 2.8), | |
| "orange": (47, 0.9, 12, 0.1, 2.4), | |
| # fats & condiments | |
| "olive oil": (884, 0, 0, 100, 0), | |
| "oil": (884, 0, 0, 100, 0), | |
| "soy sauce": (53, 8.1, 4.9, 0.1, 0.8), | |
| "honey": (304, 0.3, 82, 0, 0.2), | |
| "sugar": (387, 0, 100, 0, 0), | |
| "salt": (0, 0, 0, 0, 0), | |
| "vinegar": (18, 0, 0.9, 0, 0), | |
| } | |
| # Typical portion weight per ingredient (grams) | |
| _GRAMS: dict[str, int] = { | |
| "egg": 50, "eggs": 100, | |
| "butter": 15, | |
| "olive oil": 14, "oil": 14, | |
| "soy sauce": 15, | |
| "salt": 3, | |
| "garlic": 10, | |
| "honey": 21, | |
| "sugar": 12, | |
| "lemon": 30, "lime": 30, | |
| } | |
| _DEFAULT_GRAMS = 80 | |
| def compute_nutrition(ingredients: list[str], servings: int = 2) -> dict[str, float]: | |
| """Return per-serving macro estimates keyed to the NutritionGrid format.""" | |
| cal = prot = carb = fat = fib = 0.0 | |
| for ing in ingredients: | |
| key = ing.lower().strip() | |
| row = _MACROS.get(key) or _MACROS.get(key.split()[0]) if key else None | |
| if row is None: | |
| continue | |
| grams = _GRAMS.get(key, _DEFAULT_GRAMS) | |
| f = grams / 100 | |
| c, p, cb, ft, fb = row | |
| cal += c * f | |
| prot += p * f | |
| carb += cb * f | |
| fat += ft * f | |
| fib += fb * f | |
| sv = max(servings, 1) | |
| return { | |
| "calories": round(cal / sv), | |
| "protein_g": round(prot / sv, 1), | |
| "carbs_g": round(carb / sv, 1), | |
| "fat_g": round(fat / sv, 1), | |
| "fiber_g": round(fib / sv, 1), | |
| } | |