Cook with what you already have. Tell PantryChef your ingredients and it finds recipes and cooking tutorial videos, scores how healthy each meal is, helps you check packaged ingredients, and answers cooking questions in plain language — grounded in a retrieval-augmented (RAG) knowledge base.
Live demo: mealguidance.vercel.app
(GitHub doesn't inline-embed video from repo files — click the thumbnail to play it.)
- Discover — find recipes and YouTube tutorials ranked by how many of your ingredients they use (
/discover). Scanned products link in two ways: a "Find recipes with this" button on the product detail page, and a "From your scans" picker right on the Discover page — tap-to-add/remove chips for your scan history, collapsed to the 2 most recent with a "Show all" toggle so it doesn't crowd out the rest of the page. - Ask PantryChef — a RAG-powered chat that answers cooking and nutrition questions, grounded in a local knowledge base of meal-prep guides and nutrition facts, with cited sources (
/ask). - Scan — point your phone's camera at a barcode (or type it in) to see a product's Nutri-Score, NOVA processing group, and additives, via the open Open Food Facts database (
/scan). Camera scanning runs entirely in the browser via ZXing — no app install required. Works well for European users specifically: Nutri-Score and NOVA are EU nutrition standards, and the scanner reads EAN-13 (the standard European barcode format) out of the box. Ingredient text is shown in whatever language the product was originally logged in on Open Food Facts (not auto-translated). - Health scoring — every recipe gets an explainable A–E grade from a Nutri-Score-inspired formula over its nutrition facts (
lib/health-score/nutriScore.ts). - Meal Prep guide — a curated, MDX-driven collection of ingredients, ready-to-eat combo meals, and full weekly plans (
/meal-prep). Combo meals (e.g. avocado-egg pita, chicken & rice power bowls) include an ingredient list and assembly steps, not just storage tips, filterable by type (protein, grain, vegetable, sauce, combo). Weekly plans (/meal-prep/plans/[slug]) bundle those into five-day rotations — a Balanced Week and a higher-protein, lower-calorie Cutting Week — each with a day-by-day breakfast/lunch/dinner/snack breakdown, calorie/protein targets, and a grocery list. - My Meal Preps — record your own meal prep with a photo, notes, storage life, and tags (
/meal-prep/mine). Photos are downscaled and compressed client-side before being saved to IndexedDB (localStorage's ~5-10MB cap is too small for photos); everything stays on-device and is never uploaded anywhere.
No accounts — ingredients, favorites, scan history, and your own meal preps (including photos) are stored in your browser (localStorage + IndexedDB) only.
Next.js (App Router) + TypeScript + Tailwind CSS, deployed to Vercel:
- Recipes: Spoonacular (
lib/apis/spoonacular.ts) - Videos: YouTube Data API v3 (
lib/apis/youtube.ts) - Product scanning: Open Food Facts (
lib/apis/openFoodFacts.ts), no API key required - RAG: embeddings via Google's Generative AI API (
gemini-embedding-001, free tier — see below) + brute-force cosine similarity over a small JSON index — no vector database needed at this scale. (Local ONNX embeddings were tried first but dropped:onnxruntime-node's native binary isn't available in Vercel's serverless runtime.) - LLM: Vercel AI SDK with a pluggable provider (
lib/llm/client.ts) — see below - Tests: Vitest, covering the pure health-score and similarity-search logic
- Observability: Langfuse (optional, free tier) — traces every RAG request end-to-end (retrieval + generation), giving latency, token cost, and full input/output per call. Wired via
instrumentation.ts; left unconfigured, tracing is skipped entirely.
LLM_PROVIDER picks the provider at runtime with zero code changes — this matters because a publicly shared demo link could otherwise run up the deployer's API bill from strangers' usage:
groq(default) — free tier, safe for a public demo.anthropic— for a higher-quality personal demo (e.g. Claude).openai— also supported.
If you clone this repo, bring your own key for whichever provider you choose (see .env.example) — your usage, your bill, isolated from anyone else running their own copy.
On 2026-08-17 the app started answering every question on /ask with "PantryChef couldn't generate an answer right now." Nothing had been deployed for six days. Groq had retired llama-3.3-70b-versatile, which had been the default since the feature was built, and the API began returning:
404 The model `llama-3.3-70b-versatile` does not exist or you do not have access to it.
No Llama model remains in Groq's lineup, so the default moved to openai/gpt-oss-120b (131k context, the closest replacement). Free hosted inference is the trade here: providers rotate their catalogue without notice, and a working deployment can break with no commit behind it.
Two things this cost more time than it should have, both now fixed:
- The error was swallowed.
/api/ragcollapsed every non-config failure into one friendly sentence, so the 404 never reached the browser and finding it meant testing the embedding API and the LLM API by hand. Upstream messages now travel in adetailfield (key-shaped strings scrubbed first, sincedetailis client-visible). LLM_MODELlives in four places..env.local,.env.example, the fallback inlib/llm/client.ts, and Vercel's own store — where Production, Preview, and Development are each set separately. Fixing the first three left production broken; fixing production left a stale Development value thatvercel env pullwould have silently written back over the local fix.
If /ask breaks again, check the model still exists before anything else:
curl -s https://api.groq.com/openai/v1/models \
-H "Authorization: Bearer $GROQ_API_KEY" | grep -o '"id":"[^"]*"'git clone <this-repo-url>
cd mealguidance
npm install
cp .env.example .env.local
# fill in at least YOUTUBE_API_KEY, SPOONACULAR_API_KEY, GROQ_API_KEY, and
# GOOGLE_GENERATIVE_AI_API_KEY (all free) in .env.local
npm run ingest # builds the RAG knowledge base — data/kb/embeddings.json is already committed, but re-run after editing content/
npm run devOpen http://localhost:3000.
| Variable | Where to get it | Free tier |
|---|---|---|
YOUTUBE_API_KEY |
Google Cloud Console → enable "YouTube Data API v3" → Credentials | ~100 searches/day |
SPOONACULAR_API_KEY |
spoonacular.com/food-api | ~150 points/day |
GROQ_API_KEY |
console.groq.com | generous free tier |
GOOGLE_GENERATIVE_AI_API_KEY |
aistudio.google.com/apikey | 10M tokens/min free |
| Open Food Facts | no key needed | — |
YouTube (~100 searches/day) and Spoonacular (~150 points/day) both have small free quotas. Results are cached aggressively (7–30 day TTLs) to stretch them as far as possible, and the app degrades gracefully — e.g. if the YouTube quota is exhausted, recipe results still show without videos rather than erroring out. If you fork this for real traffic, request a quota increase from Google early (it takes a few days to approve).
Groq's free tier also has a small daily token budget (100k TPD as of writing) — a full npm run eval run uses a meaningful chunk of it, which is why the CI eval job (below) only runs when RAG-relevant paths change, not on every PR.
npm run eval runs evals/rag-eval-set.json (25 real questions against the meal-prep/meal-plan/nutrition knowledge base) through the full RAG pipeline and checks two things per question:
- Retrieval relevance — does the expected source chunk actually show up in the top-5 retrieved results?
- Answer correctness — does the generated answer contain the expected facts (checked as case-insensitive substrings, with alternate phrasings per fact so paraphrasing doesn't cause a false fail)?
It prints a pass rate for each and exits non-zero if either drops below threshold (90% retrieval / 80% correctness by default, overridable via EVAL_RETRIEVAL_THRESHOLD / EVAL_CORRECTNESS_THRESHOLD). Wired into CI (.github/workflows/ci.yml) as a required check on PRs that touch lib/rag/, lib/llm/, lib/embeddings/, content/, or the eval set itself — needs GROQ_API_KEY and GOOGLE_GENERATIVE_AI_API_KEY set as repo secrets, and skips cleanly if they aren't (e.g. on forked PRs).
npm run dev # start the dev server
npm run build # production build
npm run lint # ESLint
npm run typecheck # tsc --noEmit
npm run test # Vitest
npm run ingest # rebuild the RAG knowledge base from content/
npm run eval # run the RAG eval set (retrieval relevance + answer correctness)- Push this repo to your own GitHub account.
- Import it into Vercel.
- Add the environment variables from
.env.examplein the Vercel project settings. - Deploy —
data/kb/embeddings.jsonis committed, so no build-time ingest step is required.
app/ # Next.js App Router pages + API routes (proxy every external API call)
components/ # UI primitives (components/ui/) + feature components
lib/
apis/ # typed, cached fetch wrappers for Spoonacular / YouTube / Open Food Facts
health-score/ # pure Nutri-Score-inspired grading function (+ tests)
embeddings/ # hosted embedding model client + cosine similarity (+ tests)
rag/ # retrieval + generation over the knowledge base
llm/ # pluggable LLM provider client + prompts
content/ # Meal Prep + Meal Plan MDX parsing
storage/ # localStorage + IndexedDB helpers (no backend user data)
utils/ # small client helpers, e.g. client-side image compression
content/meal-prep/*.mdx # curated Meal Prep entries (components + combo meals) — add a file here to add one
content/meal-plans/*.mdx # curated weekly plans that reference meal-prep slugs — add a file here to add one
content/nutrition-knowledge.json # hand-authored nutrition facts fed into the RAG index
scripts/ingest.ts # builds data/kb/embeddings.json from content/
data/kb/embeddings.json # generated RAG index (committed so a fresh clone works immediately)
MIT — see LICENSE.