feat(meals): suggest complementary sides
CI / backend (pytest + alembic) (push) Has been cancelled
CI / frontend (build) (push) Has been cancelled

This commit is contained in:
2026-06-29 14:59:30 -07:00
parent 18d7300b57
commit 7f5757094e
12 changed files with 294 additions and 9 deletions
+13
View File
@@ -35,6 +35,7 @@ from app.config import settings
from app.database import get_db
from app.models import FamilyProfile, MealPlan, MealPlanItem, MealType, Recipe
from app.security import require_session
from app.services.meal_pairings import components_with_suggested_sides
logger = logging.getLogger(__name__)
@@ -257,6 +258,10 @@ def synthesize_plan(
raw_picks = _parse_picks(raw or "")
valid_recipe_ids = {r["id"] for r in library}
picks = _validate_picks(raw_picks, valid_recipe_ids)
recipe_by_id = {
str(r.id): r
for r in db.query(Recipe).filter(Recipe.id.in_([uuid.UUID(rid) for rid in valid_recipe_ids])).all()
}
logger.info(
"LLM plan: prompt=%d chars, raw_picks=%d, valid_picks=%d",
len(payload.prompt), len(raw_picks), len(picks),
@@ -279,6 +284,10 @@ def synthesize_plan(
recipe_id=uuid.UUID(pick.recipe_id),
day_of_week=pick.day_of_week,
meal_type=MealType(pick.meal_type),
components=components_with_suggested_sides(
recipe_by_id.get(pick.recipe_id),
pick.meal_type,
),
))
db.flush()
@@ -310,6 +319,10 @@ def synthesize_plan(
recipe_id=uuid.UUID(chosen["id"]),
day_of_week=day,
meal_type=MealType(mt),
components=components_with_suggested_sides(
recipe_by_id.get(chosen["id"]),
mt,
),
))
used_recipe_ids.add(uuid.UUID(chosen["id"]))
filled_count += 1
+6 -2
View File
@@ -30,6 +30,10 @@ admin_router = APIRouter(
public_router = APIRouter(prefix="/api/meal-plans", tags=["meal-plans"])
def _components_payload(components: dict | None) -> dict:
return dict(components or {})
def _to_response(week_start, plan_id, items: List[MealPlanItem], result: GenerationResult) -> GenerationResponse:
item_payloads: List[GenerationItem] = []
score_by_recipe = {s.recipe_id: s for s in result.selected}
@@ -41,7 +45,7 @@ def _to_response(week_start, plan_id, items: List[MealPlanItem], result: Generat
day_of_week=it.day_of_week,
estimated_cost=it.estimated_cost or 0,
score=scored.score if scored else 0.0,
components={k: float(v) for k, v in (scored.components.items() if scored else [])},
components=_components_payload(it.components or (scored.components if scored else None)),
)
)
return GenerationResponse(
@@ -135,7 +139,7 @@ def get_plan(plan_id: UUID, db: Session = Depends(get_db)) -> GenerationResponse
day_of_week=it.day_of_week,
estimated_cost=it.estimated_cost or 0,
score=it.score or 0.0,
components={k: float(v) for k, v in (it.components.items() if it.components else [])},
components=_components_payload(it.components),
)
for it in items
],
+3
View File
@@ -19,6 +19,7 @@ from app.schemas import (
from app.security import require_session
from app.services import approval as approval_service
from app.services.feedback_analyzer import FeedbackAnalyzer
from app.services.meal_pairings import components_with_suggested_sides
from uuid import UUID
from typing import List, Optional
from datetime import datetime, timedelta, timezone
@@ -683,6 +684,7 @@ def generate_single_item(
day_of_week=day_of_week,
meal_type=MealType[meal_type.upper()],
approval_status=MealPlanItemStatus.pending,
components=components_with_suggested_sides(recipe, meal_type),
)
db.add(new_item)
db.commit()
@@ -769,6 +771,7 @@ def fill_empty_slots(
day_of_week=day,
meal_type=MealType[mt.upper()],
approval_status=MealPlanItemStatus.pending,
components=components_with_suggested_sides(recipe, mt),
)
db.add(new_item)
try: