from typing import Any, List from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlmodel import Session, select from app.ai.plans import plan_module from app.api import deps from app.models.plan import Plan router = APIRouter() class PlanRequest(BaseModel): goal: str user_details: str # e.g., "Male, 30, 80kg" @router.post("/generate", response_model=Plan) def generate_plan( *, current_user: deps.CurrentUser, request: PlanRequest, session: Session = Depends(deps.get_session), ) -> Any: """ Generate a new diet/exercise plan using AI. """ try: # Generate plan using DSPy generated = plan_module(user_profile=request.user_details, goal=request.goal) # Determine content string (markdown representation) content_md = ( f"# {generated.plan.title}\n\n{generated.plan.summary}\n\n## Diet\n" + "\n".join([f"- {item}" for item in generated.plan.diet_plan]) + "\n\n## Exercise\n" + "\n".join([f"- {item}" for item in generated.plan.exercise_plan]) + "\n\n## Tips\n" + "\n".join([f"- {item}" for item in generated.plan.tips]) ) plan = Plan( user_id=current_user.id, goal=request.goal, content=content_md, structured_content=generated.plan.model_dump(), ) session.add(plan) session.commit() session.refresh(plan) return plan except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/", response_model=List[Plan]) def read_plans( current_user: deps.CurrentUser, session: Session = Depends(deps.get_session), ) -> Any: """ Get all plans for the current user. """ statement = select(Plan).where(Plan.user_id == current_user.id).order_by(Plan.created_at.desc()) plans = session.exec(statement).all() return plans