Spaces:
Sleeping
Sleeping
| """ | |
| SmartPlate end-to-end pipeline orchestrator. | |
| Wires together Block 1 (CV), Block 2 (ML), and Block 3 (RAG) via lazy-loading | |
| properties. Blocks are instantiated on first access; models load on first call. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| from dataclasses import dataclass | |
| from typing import Any, Dict, Optional | |
| from PIL import Image | |
| from src.cv_model import CVModel | |
| from src.ml_model import MLModel | |
| from src.nlp_rag import RAGPipeline | |
| logger = logging.getLogger(__name__) | |
| class PipelineResult: | |
| """Full result of one SmartPlate pipeline run — kept for backward compatibility.""" | |
| cv_result: Dict[str, Any] | |
| ml_result: Dict[str, Any] | |
| nlp_result: Dict[str, Any] | |
| class SmartPlatePipeline: | |
| """Orchestrates the three-block SmartPlate pipeline with lazy loading. | |
| Blocks are instantiated on first property access; model weights load on | |
| first inference call. This keeps the import fast and cold-start cheap. | |
| Example: | |
| >>> pipeline = SmartPlatePipeline() | |
| >>> result = pipeline.process(image) | |
| >>> print(result["cv_result"]["class"]) | |
| pizza | |
| """ | |
| def __init__(self) -> None: | |
| self._cv: Optional[CVModel] = None | |
| self._ml: Optional[MLModel] = None | |
| self._rag: Optional[RAGPipeline] = None | |
| def cv(self) -> CVModel: | |
| if self._cv is None: | |
| self._cv = CVModel() | |
| return self._cv | |
| def ml(self) -> MLModel: | |
| if self._ml is None: | |
| self._ml = MLModel() | |
| return self._ml | |
| def rag(self) -> RAGPipeline: | |
| if self._rag is None: | |
| self._rag = RAGPipeline() | |
| return self._rag | |
| def process( | |
| self, | |
| image: Image.Image, | |
| user_question: Optional[str] = None, | |
| ) -> Dict[str, Any]: | |
| """Run the full three-block pipeline on a food image. | |
| Args: | |
| image: PIL Image of the food item. | |
| user_question: Optional follow-up question from the user. | |
| Returns: | |
| { | |
| "image": PIL.Image, | |
| "cv_result": {"class": str, "confidence": float, "top_5": list}, | |
| "ml_result": {"food_class": str, "nutrition": dict, | |
| "health_label": str, "probabilities": dict}, | |
| "nlp_result": {"answer": str, "sources": list, "tokens": int} | |
| } | |
| """ | |
| logger.info("Running CV block ...") | |
| cv_result = self.cv.predict(image) | |
| food_class: str = cv_result["class"] | |
| logger.info("Running ML block for class: %s", food_class) | |
| ml_result = self.ml.predict(food_class) | |
| logger.info("Running RAG block ...") | |
| nlp_result = self.rag.answer( | |
| food_class=food_class, | |
| kcal=float(ml_result["nutrition"]["kcal"]), | |
| health_label=ml_result["health_label"], | |
| user_question=user_question, | |
| ) | |
| return { | |
| "image": image, | |
| "cv_result": cv_result, | |
| "ml_result": ml_result, | |
| "nlp_result": nlp_result, | |
| } | |