import logging import gradio as gr from recipe import RecipeRequest, generate_recipe from shopping_list import ( MAX_BASKET_SIZE, build_shopping_list, format_ingredient_name, get_ingredient_choices, ) logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s") logger = logging.getLogger(__name__) logger.info("Loading Epicure Core ingredient list...") INGREDIENT_CHOICES = list(get_ingredient_choices()) logger.info("Loaded %d ingredients", len(INGREDIENT_CHOICES)) def _format_steps(steps: list[str]) -> str: if not steps: return "" return "\n".join(f"{index}. {step}" for index, step in enumerate(steps, start=1)) def _format_ingredients(names: list[str]) -> str: if not names: return "" return "\n".join(f"- {name}" for name in names) def generate(ingredients: list[str]): loading_btn = gr.Button(value="Generating...", interactive=False) ready_btn = gr.Button(value="Generate", interactive=True) yield loading_btn, "", "", "", "" if not ingredients: yield ready_btn, "", "", "", "Please select at least one ingredient." return shopping = build_shopping_list(ingredients) if not shopping.resolved: message = "Could not map any ingredients to the Epicure vocabulary." if shopping.unresolved: message += f" Skipped: {', '.join(shopping.unresolved)}." yield ready_btn, "", "", "", message return original_display = [format_ingredient_name(key) for key in shopping.resolved] recipe = generate_recipe( RecipeRequest( original_ingredients=original_display, suggested_ingredients=[suggestion.name for suggestion in shopping.suggestions], ) ) if not recipe.validation_ok: yield ( ready_btn, "", "", "", recipe.error_message or "Something went wrong generating the recipe.", ) return yield ( ready_btn, f"## {recipe.title}", _format_ingredients(recipe.ingredients_used), _format_steps(recipe.steps), "", ) EPICURE_CITATION = """ --- **References** ¹ Radzikowski & Chen (2026). *Epicure: Navigating the Emergent Geometry of Food Ingredient Embeddings*. [arXiv:2605.22391](https://arxiv.org/abs/2605.22391). Model: [Kaikaku/epicure-core](https://huggingface.co/Kaikaku/epicure-core). """ with gr.Blocks(title="Meal Prep") as demo: gr.Markdown("# Meal Prep") gr.Markdown( "Select ingredients from the Epicure Core vocabulary, then click " "Generate to get a recipe. Similar-ingredient suggestions use " "[Epicure Core](https://huggingface.co/Kaikaku/epicure-core)¹." ) ingredients = gr.Dropdown( choices=INGREDIENT_CHOICES, label="Ingredients", multiselect=True, max_choices=MAX_BASKET_SIZE, filterable=True, ) generate_btn = gr.Button("Generate", variant="primary") recipe_title = gr.Markdown(label="Recipe") ingredients_used = gr.Markdown(label="Ingredients used") recipe_steps = gr.Markdown(label="Steps") error_message = gr.Markdown() generate_btn.click( generate, inputs=ingredients, outputs=[generate_btn, recipe_title, ingredients_used, recipe_steps, error_message], show_progress="full", ) gr.Markdown(EPICURE_CITATION) demo.launch()