Cook_with_a_LLM / src /data /nutrition.py
Fred1e4's picture
Complete Cook App (#5)
75c5414
"""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),
}