| title | Chat Cooking |
|---|---|
| emoji | 🍳 |
| colorFrom | yellow |
| colorTo | red |
| sdk | gradio |
| sdk_version | 6.15.2 |
| python_version | 3.11 |
| app_file | app.py |
| pinned | false |
A cooking assistant chatbot powered by RAG (Retrieval-Augmented Generation). Ask about recipes, ingredients, or cooking techniques — it semantically retrieves relevant recipes from a dataset of 1,090 dishes and streams a grounded response via the HuggingFace Inference API.
Live demo: huggingface.co/spaces/kawas8516/chat-cooking
User message
|
v
is_recipe_query() keyword + word-boundary classifier
|
Yes | No ──────────────────────────────────────────> LLM (general answer)
|
v
retrieve() FAISS IndexFlatIP over sentence-transformers embeddings
| (all-MiniLM-L6-v2, cosine similarity, top-k results)
v
build_messages() system prompt + retrieved context + conversation history
|
v
stream_response() HuggingFace Inference API (Qwen2.5-7B-Instruct, streaming)
|
v
Gradio UI chat panel (left) | retrieved recipes panel (right)
| Decision | Rationale |
|---|---|
FAISS IndexFlatIP over normalized vectors |
Exact cosine search, no approximation error on a 1K corpus |
all-MiniLM-L6-v2 embedder |
384-dim, fast on CPU, strong semantic recall for food queries |
| Keyword classifier before retrieval | Avoids embedding cost and noisy retrieval on non-food questions |
Retrieved context injected as a second system message |
Keeps it clearly separated from user history for the LLM |
| Name + ingredients and directions sent as context | The model is told not to invent recipes, so it needs the actual steps to ground on |
SCORE_THRESHOLD gate on retrieval |
Drops low-relevance matches so off-topic queries get a general answer, not spurious recipes |
| Retrieval query includes the previous user turn | Follow-ups ("make it vegetarian") retrieve in the context of the original dish |
| Index built at startup if missing | Binary files rejected by HF git; CSV stays in repo, index is ephemeral |
Measured on the 1,090-recipe dataset with python eval.py:
| Metric | Score |
|---|---|
| Classifier accuracy (14 labelled queries) | 100% |
| Retrieval Hit@3 (12 query/recipe pairs) | 92% |
| Retrieval MRR | 0.861 |
| Top-1 retrieval score (mean / min) | 0.720 / 0.592 |
Faithfulness (6 cases, LLM-as-judge, --faithfulness) |
83% |
The one retrieval miss ("chocolate cherry dessert") is a data-coverage gap — no exact match exists in the 1,090-recipe subset. MRR of 0.861 means the correct recipe is ranked #1 on average. Mean top-1 similarity of 0.720 (min 0.592) sits comfortably above the SCORE_THRESHOLD of 0.3, so relevant queries always clear the relevance gate.
Faithfulness is a small (6-case), non-deterministic LLM-judge check; the one miss adds a couple of plausible details (lemon juice, paprika) absent from the retrieved fish recipe.
Run evaluation yourself:
python eval.py # classifier + retrieval
python eval.py --faithfulness # + LLM faithfulness judge (uses tokens)
python eval.py --k 5 # evaluate Hit@5 instead
python eval.py --json out.json # save results to JSONchat-cooking/
├── app.py # Gradio Blocks UI
├── rag.py # retrieval: embedder, FAISS index, is_recipe_query()
├── llm.py # build_messages(), stream_response()
├── config.py # env-based configuration (python-dotenv)
├── build_index.py # build data/recipes.index from data/recipes.csv
├── eval.py # evaluation suite (classifier + retrieval + faithfulness)
├── data/
│ └── recipes.csv # 1,090 recipes (name, ingredients, directions, url)
├── tests/
│ ├── test_rag.py # 12 unit tests for retrieval and classifier
│ └── test_llm.py # 11 unit tests for message building and streaming
└── .github/workflows/
└── ci.yml # ruff + pytest on Python 3.11
git clone https://github.com/kawas8516/chat-cooking
cd chat-cooking
pip install -r requirements.txt
cp .env.example .env # add your HF_TOKEN
python build_index.py # embeds 1090 recipes, writes data/recipes.index (~15s)
python app.py # open http://localhost:7860| Variable | Default | Description |
|---|---|---|
HF_TOKEN |
required | HuggingFace token (Inference API access) |
MODEL_ID |
Qwen/Qwen2.5-7B-Instruct |
Generation model |
EMBED_MODEL_ID |
all-MiniLM-L6-v2 |
Embedding model for retrieval |
TOP_K |
3 |
Number of recipes retrieved per query |
SCORE_THRESHOLD |
0.3 |
Min cosine similarity for a recipe to be used as context |
MAX_NEW_TOKENS |
512 |
Max tokens per LLM response |
TEMPERATURE |
0.7 |
Sampling temperature |
Alternative models (set MODEL_ID to swap with no code changes):
meta-llama/Llama-3.2-3B-Instruct— lighter, fastermistralai/Mistral-7B-Instruct-v0.3— strong instruction following
- Fork this repo
- Create a new Space at huggingface.co/new-space — SDK: Gradio
- Push to the Space remote:
git push hf-space main - Add
HF_TOKENunder Settings > Secrets - The Space auto-builds the index on first boot — no manual step needed
pip install -r requirements-dev.txt
python -m pytest # 23 tests
ruff check . # lint
python eval.py # evaluationCI runs on every push to main via GitHub Actions (.github/workflows/ci.yml).
- UI — Gradio Blocks
- Retrieval — FAISS + sentence-transformers
- Generation — HuggingFace Inference API (streaming)
- Data — 1,090 recipes scraped from Allrecipes