feat: feedback-driven recipe discovery (auto-ingest via Spoonacular)
CI / backend (pytest + alembic) (push) Has been cancelled
CI / frontend (build) (push) Has been cancelled

This commit is contained in:
2026-05-24 13:17:39 -07:00
parent 35f736a052
commit 3885d7d0dc
20 changed files with 1962 additions and 9 deletions
@@ -24,6 +24,10 @@ from app.services.orchestrator.alerts import send_admin_alert
from app.services.planner.generate import generate_meal_plan
from app.services.scraper_service import ScraperService
from app.services.feedback_analyzer import FeedbackAnalyzer
from app.services.recipe_discovery import RecipeDiscoveryService
from app.services.recipe_ingestion import RecipeIngestionService
if TYPE_CHECKING:
from sqlalchemy.orm import Session
from app.models import WeeklyRun
@@ -456,6 +460,32 @@ def step_finalize(run: "WeeklyRun", db: "Session") -> None:
run.finalized_at = datetime.now(timezone.utc)
run.status = "completed"
# --- Feedback-driven recipe discovery ---
try:
analyzer = FeedbackAnalyzer(lookback_weeks=4)
analysis = analyzer.analyze(db, run.family_id)
run.feedback_analysis = analysis.to_dict()
if (
analysis.discovery_queries and
analysis.confidence >= 0.5
):
discovery = RecipeDiscoveryService()
candidates = discovery.discover(analysis.discovery_queries)
if candidates:
ingestion = RecipeIngestionService()
added = ingestion.ingest(db, run.family_id, candidates, analysis.to_dict())
logger.info(
"step_finalize: recipe discovery added %d new recipes for family %s",
added, run.family_id,
)
# ingestion deliberately does not commit so orchestrator
# can keep everything in one transaction
db.commit()
except Exception as exc:
# Discovery is best-effort; never block finalization
logger.warning("step_finalize: recipe discovery failed: %s", exc)
db.commit()
logger.info("step_finalize: done, %d approved meals", len(approved_items))