Answer with cooking plan and save edamam API tokens
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
from typing import Dict, Any
|
||||
from APIs.edamam import EdamamAPI
|
||||
from microwaveDishAnalyzer import MicrowaveDishAnalyzer
|
||||
from microwaveThermalEngine import MicrowaveThermalEngine, DishThermalState
|
||||
|
||||
|
||||
class MicrowaveCookPlanner:
|
||||
"""Orchestrates Edamam API, Dish Analyzer, and Thermal Engine into a single workflow."""
|
||||
|
||||
def __init__(self, cm_per_pixel: float = 0.05):
|
||||
self.edamam_api = EdamamAPI()
|
||||
self.analyzer = MicrowaveDishAnalyzer(cm_per_pixel=cm_per_pixel)
|
||||
self.engine = MicrowaveThermalEngine()
|
||||
|
||||
def _extract_edamam_data(self, edamam_resp: Dict[str, Any]) -> tuple[str, float, Dict[str, float]]:
|
||||
"""Parses Edamam Vision response to extract label, total mass, and macronutrient grams."""
|
||||
recipe = edamam_resp.get("combined", {}).get("recipe", {})
|
||||
|
||||
# Fallback to first dish if 'combined' is empty
|
||||
if not recipe and edamam_resp.get("dishes"):
|
||||
recipe = edamam_resp["dishes"][0].get("recipe", {})
|
||||
|
||||
label = recipe.get("label", "Unknown Dish")
|
||||
total_weight = float(recipe.get("totalWeight", 300.0)) # Default 300g fallback
|
||||
|
||||
nutrients = recipe.get("totalNutrients", {})
|
||||
|
||||
# Extract macronutrients in grams (Edamam nutrient codes)
|
||||
fat_g = float(nutrients.get("FAT", {}).get("quantity", 0.0))
|
||||
protein_g = float(nutrients.get("PROCNT", {}).get("quantity", 0.0))
|
||||
carbs_g = float(nutrients.get("CHOCDF", {}).get("quantity", 0.0))
|
||||
|
||||
# Water is sometimes omitted in Edamam; infer remaining mass as water if missing
|
||||
if "WATER" in nutrients:
|
||||
water_g = float(nutrients["WATER"].get("quantity", 0.0))
|
||||
else:
|
||||
water_g = max(0.0, total_weight - (fat_g + protein_g + carbs_g))
|
||||
|
||||
macros = {
|
||||
"water_g": water_g,
|
||||
"fat_g": fat_g,
|
||||
"protein_g": protein_g,
|
||||
"carbs_g": carbs_g,
|
||||
}
|
||||
|
||||
return label, total_weight, macros
|
||||
|
||||
def generate_plan(
|
||||
self,
|
||||
image_path: str,
|
||||
height_cm: float,
|
||||
initial_temp_c: float,
|
||||
microwave_wattage: int = 900
|
||||
) -> Dict[str, Any]:
|
||||
"""Main pipeline call to parse an image and return cooking parameters."""
|
||||
|
||||
# 1. Vision & Nutrient Analysis
|
||||
edamam_resp = self.edamam_api.analyze_dish_image(image_path)
|
||||
food_label, edamam_mass_g, macros = self._extract_edamam_data(edamam_resp)
|
||||
|
||||
# 2. Geometric Volume Calculation
|
||||
vol_data = self.analyzer.estimate_volume(
|
||||
image_path=image_path,
|
||||
height_cm=height_cm,
|
||||
food_label=food_label
|
||||
)
|
||||
|
||||
# 3. Mass Cross-Validation & Density Check
|
||||
mass_data = self.analyzer.reconcile_mass(
|
||||
edamam_mass_g=edamam_mass_g,
|
||||
volume_cm3=vol_data["volume_cm3"],
|
||||
food_label=food_label
|
||||
)
|
||||
final_mass_g = mass_data["final_mass_g"]
|
||||
|
||||
# 4. Thermal State Creation
|
||||
thermal_state = DishThermalState(
|
||||
food_name=food_label,
|
||||
macronutrients=macros,
|
||||
estimated_mass_g=final_mass_g,
|
||||
initial_temp_c=initial_temp_c,
|
||||
volume_cm3=vol_data["volume_cm3"]
|
||||
)
|
||||
|
||||
# 5. Cook Plan Calculation
|
||||
cook_plan = self.engine.calculate_cook_plan(
|
||||
state=thermal_state,
|
||||
microwave_wattage=microwave_wattage
|
||||
)
|
||||
|
||||
# Return consolidated output
|
||||
return {
|
||||
"dish_name": food_label,
|
||||
"reconciled_mass_g": final_mass_g,
|
||||
"mass_validation_status": mass_data["status"],
|
||||
"cook_plan": cook_plan,
|
||||
"geometry": vol_data
|
||||
}
|
||||
Reference in New Issue
Block a user