meal-prep / app.py
omkarpathak
Show loading state on Generate button while recipe runs.
267850b
Raw
History Blame Contribute Delete
3.45 kB
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()