smartplate / src /pipeline.py
Gianone's picture
feat: deploy SmartPlate full pipeline (CV + ML + NLP)
c173dc3
Raw
History Blame Contribute Delete
3.13 kB
"""
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__)
@dataclass
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
@property
def cv(self) -> CVModel:
if self._cv is None:
self._cv = CVModel()
return self._cv
@property
def ml(self) -> MLModel:
if self._ml is None:
self._ml = MLModel()
return self._ml
@property
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,
}