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.
+
+
+