diff --git a/app/models/schemas/recipe.py b/app/models/schemas/recipe.py index ebb07e7..0d2b61d 100644 --- a/app/models/schemas/recipe.py +++ b/app/models/schemas/recipe.py @@ -100,6 +100,7 @@ class RecipeRequest(BaseModel): allergies: list[str] = Field(default_factory=list) nutrition_filters: NutritionFilters = Field(default_factory=NutritionFilters) excluded_ids: list[int] = Field(default_factory=list) + exclude_ingredients: list[str] = Field(default_factory=list) shopping_mode: bool = False pantry_match_only: bool = False # when True, only return recipes with zero missing ingredients complexity_filter: str | None = None # 'easy' | 'moderate' | 'involved' — None = any diff --git a/app/services/recipe/llm_recipe.py b/app/services/recipe/llm_recipe.py index 00afb0c..18ee718 100644 --- a/app/services/recipe/llm_recipe.py +++ b/app/services/recipe/llm_recipe.py @@ -68,6 +68,9 @@ class LLMRecipeGenerator: if allergy_list: lines.append(f"IMPORTANT — must NOT contain: {', '.join(allergy_list)}") + if req.exclude_ingredients: + lines.append(f"IMPORTANT — user does not want these today: {', '.join(req.exclude_ingredients)}. Do not include them.") + lines.append("") lines.append(f"Covered culinary elements: {', '.join(covered_elements) or 'none'}") @@ -124,6 +127,9 @@ class LLMRecipeGenerator: if allergy_list: lines.append(f"Must NOT contain: {', '.join(allergy_list)}") + if req.exclude_ingredients: + lines.append(f"Do not use today: {', '.join(req.exclude_ingredients)}") + unit_line = ( "Use metric units (grams, ml, Celsius) for all quantities and temperatures." if req.unit_system == "metric" diff --git a/app/services/recipe/recipe_engine.py b/app/services/recipe/recipe_engine.py index d8bdbca..a594436 100644 --- a/app/services/recipe/recipe_engine.py +++ b/app/services/recipe/recipe_engine.py @@ -672,6 +672,7 @@ class RecipeEngine: profiles = self._classifier.classify_batch(req.pantry_items) gaps = self._classifier.identify_gaps(profiles) pantry_set = _expand_pantry_set(req.pantry_items, req.secondary_pantry_items or None) + exclude_set = _expand_pantry_set(req.exclude_ingredients) if req.exclude_ingredients else set() if req.level >= 3: from app.services.recipe.llm_recipe import LLMRecipeGenerator @@ -715,6 +716,10 @@ class RecipeEngine: except Exception: ingredient_names = [] + # Skip recipes that require any ingredient the user has excluded. + if exclude_set and any(_ingredient_in_pantry(n, exclude_set) for n in ingredient_names): + continue + # Compute missing ingredients, detecting pantry coverage first. # When covered, collect any prep-state annotations (e.g. "melted butter" # → note "Melt the butter before starting.") to surface separately. diff --git a/frontend/src/components/RecipesView.vue b/frontend/src/components/RecipesView.vue index 7671b29..f4a87c9 100644 --- a/frontend/src/components/RecipesView.vue +++ b/frontend/src/components/RecipesView.vue @@ -169,6 +169,31 @@ No recipes containing these ingredients will appear. + +
+ +
+ + {{ tag }} + + +
+ + Recipes containing these won't appear. Press Enter or comma to add. +
+