yuting111222's picture
Update health assistant minimal with new services and improvements
a608ddf
# 檔案路徑: app/routers/ai_router.py
from fastapi import APIRouter, File, UploadFile, HTTPException
from pydantic import BaseModel
from typing import Dict, Any, List, Optional
import logging
# 導入新的整合服務
from ..services.integrated_food_analysis_service import analyze_food_image_integrated
# 設置日誌
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
router = APIRouter(
prefix="/ai",
tags=["AI"],
)
# 新增 Pydantic 模型定義
class IntegratedAnalysisResponse(BaseModel):
success: bool
analysis_time: float
food_analysis: Dict[str, Any]
reference_analysis: Dict[str, Any]
weight_analysis: Dict[str, Any]
nutrition_analysis: Dict[str, Any]
summary: Dict[str, Any]
architecture: Dict[str, str]
class WeightEstimationResponse(BaseModel):
food_type: str
estimated_weight: float
weight_confidence: float
weight_error_range: List[float]
nutrition: Dict[str, Any]
reference_object: Optional[str] = None
note: str
class FoodAnalysisResponse(BaseModel):
food_name: str
nutrition_info: Dict[str, Any]
@router.post("/analyze-food-image/")
async def analyze_food_image_endpoint(file: UploadFile = File(...)):
"""
這個端點接收使用者上傳的食物圖片,使用 AI 模型進行辨識,
並返回辨識出的食物名稱。
"""
# 檢查上傳的檔案是否為圖片格式
if not file.content_type or not file.content_type.startswith("image/"):
raise HTTPException(status_code=400, detail="上傳的檔案不是圖片格式。")
try:
# 讀取圖片數據
image_bytes = await file.read()
# 使用新的整合服務進行分析
result = analyze_food_image_integrated(image_bytes, debug=False)
if not result.get("success", False):
raise HTTPException(status_code=500, detail=result.get("error_message", "分析失敗"))
# 返回簡化的結果
return {
"food_name": result["food_analysis"]["food_name"],
"nutrition_info": result["nutrition_analysis"]["adjusted_nutrition"]
}
except Exception as e:
logger.error(f"食物分析失敗: {str(e)}")
raise HTTPException(status_code=500, detail=f"分析失敗: {str(e)}")
@router.post("/analyze-food-image-with-weight/", response_model=WeightEstimationResponse)
async def analyze_food_image_with_weight_endpoint(file: UploadFile = File(...)):
"""
整合食物辨識、重量估算與營養分析的端點。
使用新的架構:FOOD101 → YOLO(參考物) → SAM+DPT → USDA API
"""
# 檢查上傳的檔案是否為圖片格式
if not file.content_type or not file.content_type.startswith("image/"):
raise HTTPException(status_code=400, detail="上傳的檔案不是圖片格式。")
try:
# 讀取圖片數據
image_bytes = await file.read()
# 使用新的整合服務進行分析
result = analyze_food_image_integrated(image_bytes, debug=False)
if not result.get("success", False):
raise HTTPException(status_code=500, detail=result.get("error_message", "分析失敗"))
# 轉換為舊格式以保持向後兼容
weight_analysis = result["weight_analysis"]
nutrition_analysis = result["nutrition_analysis"]
return WeightEstimationResponse(
food_type=result["food_analysis"]["food_name"],
estimated_weight=weight_analysis["estimated_weight"],
weight_confidence=weight_analysis["weight_confidence"],
weight_error_range=weight_analysis["weight_error_range"],
nutrition=nutrition_analysis["adjusted_nutrition"],
reference_object=weight_analysis["reference_object"],
note=f"使用新架構分析,耗時 {result['analysis_time']} 秒"
)
except Exception as e:
logger.error(f"重量估算分析失敗: {str(e)}")
raise HTTPException(status_code=500, detail=f"分析失敗: {str(e)}")
@router.post("/analyze-food-image-integrated/")
async def analyze_food_image_integrated_endpoint(file: UploadFile = File(...)):
"""
新的整合分析端點,返回完整的分析結果
架構:FOOD101 → YOLO(參考物) → SAM+DPT → USDA API
"""
# 檢查上傳的檔案是否為圖片格式
if not file.content_type or not file.content_type.startswith("image/"):
raise HTTPException(status_code=400, detail="上傳的檔案不是圖片格式。")
try:
# 讀取圖片數據
image_bytes = await file.read()
# 使用新的整合服務進行分析
result = analyze_food_image_integrated(image_bytes, debug=False)
return result
except Exception as e:
logger.error(f"整合分析失敗: {str(e)}")
raise HTTPException(status_code=500, detail=f"分析失敗: {str(e)}")
@router.get("/health")
async def health_check():
"""
健康檢查端點,確認 AI 服務是否正常運作
"""
return {
"status": "healthy",
"services": {
"food_classification": "available (FOOD101)",
"reference_detection": "available (YOLO)",
"weight_estimation": "available (SAM+DPT)",
"nutrition_api": "available (USDA)",
"integrated_analysis": "available"
},
"architecture": {
"layer_1": "FOOD101 (食物識別)",
"layer_2": "YOLO (參考物偵測)",
"layer_3": "SAM+DPT (重量計算)",
"layer_4": "USDA API (營養查詢)",
"layer_5": "重量調整 (營養計算)"
}
}