Spaces:
Sleeping
Sleeping
Commit ·
453dfb0
0
Parent(s):
PlateMate: AI food recognition with RAG-powered nutrition advice
Browse files- Streamlit app with camera + upload support (mobile-friendly)
- HuggingFace Transformers food image classifier (local inference)
- ChromaDB RAG pipeline with 49 curated nutrition documents
- Google Gemini 3 Flash for ingredient + healthier alternative generation
- Ready for HuggingFace Spaces deployment
Made-with: Cursor
- .gitignore +9 -0
- README.md +54 -0
- app.py +172 -0
- backend/__init__.py +0 -0
- backend/classifier.py +20 -0
- backend/config.py +16 -0
- backend/rag.py +95 -0
- backend/seed_data.py +545 -0
- requirements.txt +7 -0
.gitignore
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__/
|
| 2 |
+
*.pyc
|
| 3 |
+
.env
|
| 4 |
+
chroma_db/
|
| 5 |
+
.streamlit/secrets.toml
|
| 6 |
+
*.egg-info/
|
| 7 |
+
.venv/
|
| 8 |
+
venv/
|
| 9 |
+
.DS_Store
|
README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: PlateMate
|
| 3 |
+
emoji: 🍽️
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: yellow
|
| 6 |
+
sdk: streamlit
|
| 7 |
+
sdk_version: "1.44.1"
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
short_description: AI food recognition with RAG-powered nutrition advice
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# PlateMate — AI-Powered Culinary Assistant
|
| 14 |
+
|
| 15 |
+
PlateMate identifies foods from photos and provides nutritional insights using a **Retrieval-Augmented Generation (RAG)** pipeline. Upload or snap an image of your meal and get the detected food, its ingredients, and healthier alternatives backed by a curated nutrition knowledge base.
|
| 16 |
+
|
| 17 |
+
## Architecture
|
| 18 |
+
|
| 19 |
+
```
|
| 20 |
+
User (upload / camera)
|
| 21 |
+
│
|
| 22 |
+
▼
|
| 23 |
+
┌────────────────────────────┐
|
| 24 |
+
│ HuggingFace Transformers │ Food image classification (local)
|
| 25 |
+
│ food-image-classification │
|
| 26 |
+
└────────────┬───────────────┘
|
| 27 |
+
│
|
| 28 |
+
▼
|
| 29 |
+
┌────────────────────────────┐
|
| 30 |
+
│ Google Gemini 3 Flash │ Ingredient generation
|
| 31 |
+
└────────────┬───────────────┘
|
| 32 |
+
│
|
| 33 |
+
▼
|
| 34 |
+
┌────────────────────────────┐
|
| 35 |
+
│ ChromaDB vector search │ Retrieve top-5 nutrition docs
|
| 36 |
+
│ + Gemini 3 Flash (RAG) │ Generate grounded response
|
| 37 |
+
└────────────────────────────┘
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
## Tech Stack
|
| 41 |
+
|
| 42 |
+
| Layer | Technology |
|
| 43 |
+
|---------------|------------------------------------------------ |
|
| 44 |
+
| Frontend | Streamlit |
|
| 45 |
+
| ML Inference | HuggingFace Transformers (image classification) |
|
| 46 |
+
| LLM | Google Gemini 3 Flash |
|
| 47 |
+
| Vector DB | ChromaDB (cosine similarity) |
|
| 48 |
+
| RAG Pipeline | Retrieve from ChromaDB → augment prompt → Gemini|
|
| 49 |
+
|
| 50 |
+
## How the RAG Pipeline Works
|
| 51 |
+
|
| 52 |
+
1. **Indexing** — On startup, 49 curated nutrition and recipe documents are embedded and stored in ChromaDB.
|
| 53 |
+
2. **Retrieval** — When a user queries for healthier alternatives, the top-5 most semantically relevant documents are retrieved via cosine similarity.
|
| 54 |
+
3. **Generation** — Retrieved context is injected into a prompt sent to Gemini 3 Flash, producing a grounded response.
|
app.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from PIL import Image
|
| 3 |
+
|
| 4 |
+
from backend.classifier import classify_image
|
| 5 |
+
from backend.rag import get_ingredients, get_healthier_alternatives, seed_collection
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@st.cache_resource
|
| 9 |
+
def _init():
|
| 10 |
+
seed_collection()
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
_init()
|
| 14 |
+
|
| 15 |
+
st.set_page_config(
|
| 16 |
+
page_title="PlateMate",
|
| 17 |
+
page_icon="🍽️",
|
| 18 |
+
layout="centered",
|
| 19 |
+
initial_sidebar_state="collapsed",
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
st.markdown(
|
| 23 |
+
"""
|
| 24 |
+
<style>
|
| 25 |
+
/* ---------- Base ---------- */
|
| 26 |
+
.main-title {
|
| 27 |
+
text-align: center;
|
| 28 |
+
font-size: 2.6rem;
|
| 29 |
+
font-weight: 800;
|
| 30 |
+
background: linear-gradient(135deg, #2E7D32, #66BB6A);
|
| 31 |
+
-webkit-background-clip: text;
|
| 32 |
+
-webkit-text-fill-color: transparent;
|
| 33 |
+
margin-bottom: 0;
|
| 34 |
+
}
|
| 35 |
+
.subtitle {
|
| 36 |
+
text-align: center;
|
| 37 |
+
color: #777;
|
| 38 |
+
font-size: 1.1rem;
|
| 39 |
+
margin-top: 0;
|
| 40 |
+
margin-bottom: 1.5rem;
|
| 41 |
+
}
|
| 42 |
+
.section-header {
|
| 43 |
+
font-size: 1.25rem;
|
| 44 |
+
font-weight: 600;
|
| 45 |
+
color: #333;
|
| 46 |
+
border-bottom: 2px solid #E8F5E9;
|
| 47 |
+
padding-bottom: 0.4rem;
|
| 48 |
+
margin-top: 1.5rem;
|
| 49 |
+
}
|
| 50 |
+
div[data-testid="stMetric"] {
|
| 51 |
+
background: #F1F8E9;
|
| 52 |
+
border-radius: 10px;
|
| 53 |
+
padding: 12px 16px;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
/* ---------- Mobile (<640px) ---------- */
|
| 57 |
+
@media (max-width: 640px) {
|
| 58 |
+
.main-title { font-size: 2rem; }
|
| 59 |
+
.subtitle { font-size: 0.95rem; }
|
| 60 |
+
|
| 61 |
+
/* Full-width buttons & inputs */
|
| 62 |
+
button, .stButton > button,
|
| 63 |
+
[data-testid="stFileUploader"],
|
| 64 |
+
[data-testid="stCameraInput"] {
|
| 65 |
+
width: 100% !important;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
/* Prevent iOS zoom on input focus */
|
| 69 |
+
input, select, textarea { font-size: 16px !important; }
|
| 70 |
+
|
| 71 |
+
/* Larger tap targets */
|
| 72 |
+
.stButton > button {
|
| 73 |
+
min-height: 3rem;
|
| 74 |
+
font-size: 1.05rem !important;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
/* Stack metric cards vertically */
|
| 78 |
+
[data-testid="stHorizontalBlock"] {
|
| 79 |
+
flex-direction: column !important;
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
</style>
|
| 83 |
+
""",
|
| 84 |
+
unsafe_allow_html=True,
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
st.markdown('<p class="main-title">🍽️ PlateMate</p>', unsafe_allow_html=True)
|
| 88 |
+
st.markdown(
|
| 89 |
+
'<p class="subtitle">Snap or upload a food photo for AI-powered nutritional insights</p>',
|
| 90 |
+
unsafe_allow_html=True,
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
with st.sidebar:
|
| 94 |
+
st.header("About")
|
| 95 |
+
st.write(
|
| 96 |
+
"**PlateMate** uses computer vision to identify foods, "
|
| 97 |
+
"then retrieves nutritional advice from a curated knowledge base "
|
| 98 |
+
"using Retrieval-Augmented Generation (RAG)."
|
| 99 |
+
)
|
| 100 |
+
st.divider()
|
| 101 |
+
st.markdown(
|
| 102 |
+
"**Tech stack:** FastAPI · ChromaDB · HuggingFace Transformers · "
|
| 103 |
+
"Google Gemini · Streamlit"
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
# --------------- Image input ---------------
|
| 107 |
+
tab_upload, tab_camera = st.tabs(["📁 Upload", "📷 Camera"])
|
| 108 |
+
|
| 109 |
+
with tab_upload:
|
| 110 |
+
uploaded_file = st.file_uploader(
|
| 111 |
+
"Upload a food image",
|
| 112 |
+
type=["jpg", "jpeg", "png", "webp"],
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
with tab_camera:
|
| 116 |
+
camera_photo = st.camera_input("Take a photo of your food")
|
| 117 |
+
|
| 118 |
+
image = None
|
| 119 |
+
if uploaded_file is not None:
|
| 120 |
+
image = Image.open(uploaded_file).convert("RGB")
|
| 121 |
+
elif camera_photo is not None:
|
| 122 |
+
image = Image.open(camera_photo).convert("RGB")
|
| 123 |
+
|
| 124 |
+
if image is not None:
|
| 125 |
+
st.image(image, caption="Your image", use_container_width=True)
|
| 126 |
+
|
| 127 |
+
if st.button("🔍 Analyze", type="primary", use_container_width=True):
|
| 128 |
+
with st.spinner("Classifying image..."):
|
| 129 |
+
predictions = classify_image(image)
|
| 130 |
+
|
| 131 |
+
if not predictions:
|
| 132 |
+
st.error("Classification returned no results.")
|
| 133 |
+
else:
|
| 134 |
+
top_food = predictions[0]["label"]
|
| 135 |
+
confidence = predictions[0]["confidence"]
|
| 136 |
+
|
| 137 |
+
st.markdown(
|
| 138 |
+
'<p class="section-header">🎯 Classification</p>',
|
| 139 |
+
unsafe_allow_html=True,
|
| 140 |
+
)
|
| 141 |
+
col1, col2 = st.columns(2)
|
| 142 |
+
col1.metric("Detected Food", top_food.replace("_", " ").title())
|
| 143 |
+
col2.metric("Confidence", f"{confidence * 100:.1f}%")
|
| 144 |
+
|
| 145 |
+
if len(predictions) > 1:
|
| 146 |
+
with st.expander("Other possibilities"):
|
| 147 |
+
for p in predictions[1:]:
|
| 148 |
+
st.write(
|
| 149 |
+
f"- **{p['label'].replace('_', ' ').title()}** "
|
| 150 |
+
f"— {p['confidence'] * 100:.1f}%"
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
with st.spinner("Fetching ingredients..."):
|
| 154 |
+
ingredients = get_ingredients(top_food)
|
| 155 |
+
st.markdown(
|
| 156 |
+
'<p class="section-header">📝 Ingredients</p>',
|
| 157 |
+
unsafe_allow_html=True,
|
| 158 |
+
)
|
| 159 |
+
st.write(ingredients)
|
| 160 |
+
|
| 161 |
+
with st.spinner("Generating healthier alternatives via RAG..."):
|
| 162 |
+
alternatives, sources_used = get_healthier_alternatives(top_food)
|
| 163 |
+
st.markdown(
|
| 164 |
+
'<p class="section-header">💡 Healthier Alternatives</p>',
|
| 165 |
+
unsafe_allow_html=True,
|
| 166 |
+
)
|
| 167 |
+
st.info(
|
| 168 |
+
f"Retrieved from {sources_used} knowledge-base documents via RAG"
|
| 169 |
+
)
|
| 170 |
+
st.write(alternatives)
|
| 171 |
+
else:
|
| 172 |
+
st.info("👆 Upload a food image or snap a photo to get started.")
|
backend/__init__.py
ADDED
|
File without changes
|
backend/classifier.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from PIL import Image
|
| 2 |
+
from transformers import pipeline
|
| 3 |
+
from functools import lru_cache
|
| 4 |
+
|
| 5 |
+
from .config import get_settings
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@lru_cache(maxsize=1)
|
| 9 |
+
def _load_pipeline():
|
| 10 |
+
settings = get_settings()
|
| 11 |
+
return pipeline("image-classification", model=settings.classification_model)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def classify_image(image: Image.Image, top_k: int = 5) -> list[dict]:
|
| 15 |
+
pipe = _load_pipeline()
|
| 16 |
+
predictions = pipe(image)
|
| 17 |
+
return [
|
| 18 |
+
{"label": p["label"], "confidence": round(p["score"], 4)}
|
| 19 |
+
for p in predictions[:top_k]
|
| 20 |
+
]
|
backend/config.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic_settings import BaseSettings
|
| 2 |
+
from functools import lru_cache
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class Settings(BaseSettings):
|
| 6 |
+
gemini_api_key: str = ""
|
| 7 |
+
chroma_persist_dir: str = "./chroma_db"
|
| 8 |
+
classification_model: str = "Shresthadev403/food-image-classification"
|
| 9 |
+
gemini_model: str = "gemini-3-flash-preview"
|
| 10 |
+
|
| 11 |
+
model_config = {"env_file": ".env"}
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
@lru_cache
|
| 15 |
+
def get_settings() -> Settings:
|
| 16 |
+
return Settings()
|
backend/rag.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import logging
|
| 4 |
+
|
| 5 |
+
import chromadb
|
| 6 |
+
from google import genai
|
| 7 |
+
|
| 8 |
+
from .config import get_settings
|
| 9 |
+
from .seed_data import DOCUMENTS
|
| 10 |
+
|
| 11 |
+
logger = logging.getLogger(__name__)
|
| 12 |
+
|
| 13 |
+
_chroma_client: chromadb.PersistentClient | None = None
|
| 14 |
+
_collection: chromadb.Collection | None = None
|
| 15 |
+
_gemini_client: genai.Client | None = None
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def _get_collection() -> chromadb.Collection:
|
| 19 |
+
global _chroma_client, _collection
|
| 20 |
+
if _collection is None:
|
| 21 |
+
settings = get_settings()
|
| 22 |
+
_chroma_client = chromadb.PersistentClient(path=settings.chroma_persist_dir)
|
| 23 |
+
_collection = _chroma_client.get_or_create_collection(
|
| 24 |
+
name="recipes",
|
| 25 |
+
metadata={"hnsw:space": "cosine"},
|
| 26 |
+
)
|
| 27 |
+
return _collection
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def _get_gemini_client() -> genai.Client:
|
| 31 |
+
global _gemini_client
|
| 32 |
+
if _gemini_client is None:
|
| 33 |
+
settings = get_settings()
|
| 34 |
+
_gemini_client = genai.Client(api_key=settings.gemini_api_key)
|
| 35 |
+
return _gemini_client
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def seed_collection() -> None:
|
| 39 |
+
collection = _get_collection()
|
| 40 |
+
if collection.count() > 0:
|
| 41 |
+
logger.info("ChromaDB already seeded (%d documents).", collection.count())
|
| 42 |
+
return
|
| 43 |
+
|
| 44 |
+
ids = [f"doc_{i}" for i in range(len(DOCUMENTS))]
|
| 45 |
+
collection.add(
|
| 46 |
+
documents=[d["text"] for d in DOCUMENTS],
|
| 47 |
+
metadatas=[{"category": d["category"], "food": d["food"]} for d in DOCUMENTS],
|
| 48 |
+
ids=ids,
|
| 49 |
+
)
|
| 50 |
+
logger.info("Seeded ChromaDB with %d recipe/nutrition documents.", len(DOCUMENTS))
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def get_ingredients(food_name: str) -> str:
|
| 54 |
+
client = _get_gemini_client()
|
| 55 |
+
settings = get_settings()
|
| 56 |
+
|
| 57 |
+
response = client.models.generate_content(
|
| 58 |
+
model=settings.gemini_model,
|
| 59 |
+
contents=(
|
| 60 |
+
f"List only the main ingredients for {food_name}. "
|
| 61 |
+
"Respond in a concise, comma-separated list without extra text."
|
| 62 |
+
),
|
| 63 |
+
)
|
| 64 |
+
return response.text.strip()
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def get_healthier_alternatives(food_name: str) -> tuple[str, int]:
|
| 68 |
+
collection = _get_collection()
|
| 69 |
+
client = _get_gemini_client()
|
| 70 |
+
settings = get_settings()
|
| 71 |
+
|
| 72 |
+
results = collection.query(
|
| 73 |
+
query_texts=[f"healthy alternative for {food_name}"],
|
| 74 |
+
n_results=5,
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
context_docs = results["documents"][0] if results["documents"] else []
|
| 78 |
+
context = "\n\n".join(context_docs)
|
| 79 |
+
sources_used = len(context_docs)
|
| 80 |
+
|
| 81 |
+
prompt = (
|
| 82 |
+
"You are a knowledgeable nutritionist and chef. Use the provided context "
|
| 83 |
+
"to give specific, actionable advice about healthier versions of foods. "
|
| 84 |
+
"Include calorie comparisons and a brief recipe when possible. "
|
| 85 |
+
"If the context doesn't cover the exact food, use relevant principles from it.\n\n"
|
| 86 |
+
f"Context from nutrition knowledge base:\n\n{context}\n\n"
|
| 87 |
+
f"What are healthier alternatives or modifications for {food_name}? "
|
| 88 |
+
f"Include a brief healthy recipe suggestion."
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
response = client.models.generate_content(
|
| 92 |
+
model=settings.gemini_model,
|
| 93 |
+
contents=prompt,
|
| 94 |
+
)
|
| 95 |
+
return response.text.strip(), sources_used
|
backend/seed_data.py
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
DOCUMENTS = [
|
| 2 |
+
# --- Pizza & Italian ---
|
| 3 |
+
{
|
| 4 |
+
"food": "pizza",
|
| 5 |
+
"category": "italian",
|
| 6 |
+
"text": (
|
| 7 |
+
"Traditional pizza dough is made from refined white flour, which spikes blood sugar. "
|
| 8 |
+
"A healthier alternative uses whole-wheat flour or a cauliflower crust, cutting carbs by up to 60%. "
|
| 9 |
+
"Top with fresh vegetables like bell peppers, spinach, and mushrooms instead of processed meats. "
|
| 10 |
+
"Use part-skim mozzarella or nutritional yeast to reduce saturated fat. A thin-crust vegetable pizza "
|
| 11 |
+
"with a drizzle of olive oil provides fiber, vitamins, and healthy fats while keeping calories around 200 per slice."
|
| 12 |
+
),
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"food": "pizza",
|
| 16 |
+
"category": "italian",
|
| 17 |
+
"text": (
|
| 18 |
+
"Cauliflower crust pizza recipe: pulse one head of cauliflower into rice-sized pieces, microwave 4 minutes, "
|
| 19 |
+
"squeeze out moisture, mix with one egg and 1/2 cup mozzarella. Press onto a parchment-lined baking sheet "
|
| 20 |
+
"and bake at 425°F for 12 minutes. Top with marinara, fresh basil, cherry tomatoes, and a sprinkle of feta. "
|
| 21 |
+
"This version has roughly 150 calories per slice compared to 300+ for traditional pizza."
|
| 22 |
+
),
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"food": "pasta",
|
| 26 |
+
"category": "italian",
|
| 27 |
+
"text": (
|
| 28 |
+
"Swap regular pasta for whole-grain, chickpea, or lentil-based pasta to increase protein and fiber. "
|
| 29 |
+
"Chickpea pasta contains about 25g protein per serving compared to 7g in white pasta. "
|
| 30 |
+
"Replace cream-based sauces with marinara or pesto made from fresh basil, pine nuts, and olive oil. "
|
| 31 |
+
"Add roasted vegetables like zucchini, eggplant, and cherry tomatoes for volume without excess calories."
|
| 32 |
+
),
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"food": "lasagna",
|
| 36 |
+
"category": "italian",
|
| 37 |
+
"text": (
|
| 38 |
+
"Healthy lasagna substitutes: use thinly sliced zucchini or eggplant in place of pasta sheets. "
|
| 39 |
+
"Replace ricotta with blended cottage cheese for higher protein and lower fat. Use ground turkey "
|
| 40 |
+
"or lentils instead of ground beef to cut saturated fat by 50%. Layer with spinach and roasted "
|
| 41 |
+
"red peppers for extra vitamins. A veggie-forward lasagna can have 250 calories per serving "
|
| 42 |
+
"compared to 400+ for traditional versions."
|
| 43 |
+
),
|
| 44 |
+
},
|
| 45 |
+
# --- Burgers & Sandwiches ---
|
| 46 |
+
{
|
| 47 |
+
"food": "hamburger",
|
| 48 |
+
"category": "burgers",
|
| 49 |
+
"text": (
|
| 50 |
+
"A standard fast-food burger has 500-700 calories with high saturated fat. Healthier alternatives: "
|
| 51 |
+
"use lean ground turkey (93% lean) or make black bean patties blended with oats and spices. "
|
| 52 |
+
"Wrap in lettuce leaves instead of a white bun, or use a whole-grain bun. Top with avocado slices "
|
| 53 |
+
"instead of cheese, and add plenty of fresh vegetables. A turkey burger in a lettuce wrap "
|
| 54 |
+
"with avocado runs about 300 calories with 25g protein."
|
| 55 |
+
),
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"food": "hamburger",
|
| 59 |
+
"category": "burgers",
|
| 60 |
+
"text": (
|
| 61 |
+
"Black bean burger recipe: mash one can of black beans, mix with 1/2 cup oats, one egg, "
|
| 62 |
+
"diced onion, garlic, cumin, and smoked paprika. Form into patties and bake at 375°F for "
|
| 63 |
+
"10 minutes per side. Each patty has about 180 calories, 10g protein, and 8g fiber. "
|
| 64 |
+
"Serve on a whole-wheat bun with arugula, tomato, and a tahini drizzle."
|
| 65 |
+
),
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"food": "sandwich",
|
| 69 |
+
"category": "burgers",
|
| 70 |
+
"text": (
|
| 71 |
+
"Upgrade sandwiches by swapping white bread for whole-grain or sourdough, which has a lower "
|
| 72 |
+
"glycemic index. Use hummus or mashed avocado instead of mayo to add fiber and healthy fats. "
|
| 73 |
+
"Choose grilled chicken or turkey breast over deli meats, which are high in sodium and nitrates. "
|
| 74 |
+
"Load up on vegetables: spinach, cucumber, roasted peppers, and sprouts add crunch and nutrients."
|
| 75 |
+
),
|
| 76 |
+
},
|
| 77 |
+
# --- Asian Cuisine ---
|
| 78 |
+
{
|
| 79 |
+
"food": "sushi",
|
| 80 |
+
"category": "asian",
|
| 81 |
+
"text": (
|
| 82 |
+
"Sushi can be healthy but watch portion sizes of white rice, which is high-glycemic. "
|
| 83 |
+
"Request brown rice sushi for more fiber and nutrients. Choose sashimi (just fish) to skip "
|
| 84 |
+
"rice entirely. Avoid tempura rolls and spicy mayo, which add 200+ calories per roll. "
|
| 85 |
+
"Opt for rolls with salmon, tuna, or vegetables. Edamame is an excellent protein-rich side. "
|
| 86 |
+
"A salmon avocado roll with brown rice has about 250 calories with omega-3 fatty acids."
|
| 87 |
+
),
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"food": "ramen",
|
| 91 |
+
"category": "asian",
|
| 92 |
+
"text": (
|
| 93 |
+
"Traditional ramen broth is rich but calorie-dense. For a lighter version, use a miso or "
|
| 94 |
+
"chicken bone broth base with less sodium. Swap instant noodles for shirataki (konjac) noodles "
|
| 95 |
+
"which have nearly zero calories, or use whole-wheat ramen. Add soft-boiled egg for protein, "
|
| 96 |
+
"plenty of bok choy, mushrooms, and scallions. A bowl of miso ramen with shirataki noodles "
|
| 97 |
+
"and vegetables has about 200 calories compared to 500+ for standard tonkotsu ramen."
|
| 98 |
+
),
|
| 99 |
+
},
|
| 100 |
+
{
|
| 101 |
+
"food": "fried rice",
|
| 102 |
+
"category": "asian",
|
| 103 |
+
"text": (
|
| 104 |
+
"Fried rice is typically made with day-old white rice and lots of oil. Healthier version: "
|
| 105 |
+
"use cauliflower rice or brown rice, cook with just one tablespoon of sesame oil, and load "
|
| 106 |
+
"with vegetables like peas, carrots, corn, and edamame. Use coconut aminos or low-sodium soy sauce. "
|
| 107 |
+
"Add scrambled egg or tofu for protein. Cauliflower fried rice has about 150 calories per cup "
|
| 108 |
+
"compared to 350+ for traditional fried rice."
|
| 109 |
+
),
|
| 110 |
+
},
|
| 111 |
+
{
|
| 112 |
+
"food": "pad thai",
|
| 113 |
+
"category": "asian",
|
| 114 |
+
"text": (
|
| 115 |
+
"Pad Thai is high in calories from rice noodles, sugar, and oil. A healthier version uses "
|
| 116 |
+
"spiralized zucchini or sweet potato noodles, replaces sugar with a small amount of honey "
|
| 117 |
+
"or date paste, and uses less oil. Add shrimp or tofu for lean protein, plenty of bean sprouts, "
|
| 118 |
+
"and crushed peanuts for healthy fats. Zucchini noodle pad thai runs about 250 calories "
|
| 119 |
+
"compared to 400+ for the original."
|
| 120 |
+
),
|
| 121 |
+
},
|
| 122 |
+
# --- Salads & Vegetables ---
|
| 123 |
+
{
|
| 124 |
+
"food": "salad",
|
| 125 |
+
"category": "salads",
|
| 126 |
+
"text": (
|
| 127 |
+
"Not all salads are healthy — Caesar salad with dressing can exceed 600 calories. "
|
| 128 |
+
"Build a nutritious salad: start with dark leafy greens (spinach, kale, arugula) "
|
| 129 |
+
"which have more nutrients than iceberg lettuce. Add lean protein (grilled chicken, "
|
| 130 |
+
"chickpeas, or hard-boiled eggs), healthy fats (avocado, nuts, olive oil), and fiber "
|
| 131 |
+
"(quinoa, roasted sweet potato). Use vinaigrette instead of creamy dressings."
|
| 132 |
+
),
|
| 133 |
+
},
|
| 134 |
+
{
|
| 135 |
+
"food": "salad",
|
| 136 |
+
"category": "salads",
|
| 137 |
+
"text": (
|
| 138 |
+
"Power salad recipe: combine 2 cups mixed greens, 1/2 cup quinoa, 4 oz grilled chicken, "
|
| 139 |
+
"1/4 avocado, cherry tomatoes, cucumber, and toasted almonds. Dress with lemon-tahini "
|
| 140 |
+
"dressing (2 tbsp tahini, juice of one lemon, 1 tbsp water, pinch of salt). "
|
| 141 |
+
"This meal has about 450 calories, 35g protein, 10g fiber, and covers multiple food groups."
|
| 142 |
+
),
|
| 143 |
+
},
|
| 144 |
+
{
|
| 145 |
+
"food": "caesar salad",
|
| 146 |
+
"category": "salads",
|
| 147 |
+
"text": (
|
| 148 |
+
"Lighten Caesar salad: replace croutons with roasted chickpeas (more protein, more fiber). "
|
| 149 |
+
"Make dressing from Greek yogurt, lemon juice, garlic, and a small amount of Parmesan "
|
| 150 |
+
"instead of the traditional egg-and-oil version. Use romaine hearts with added kale for "
|
| 151 |
+
"extra nutrients. Add grilled shrimp or salmon instead of just cheese. "
|
| 152 |
+
"This version cuts calories by 40% while adding 15g more protein."
|
| 153 |
+
),
|
| 154 |
+
},
|
| 155 |
+
# --- Breakfast ---
|
| 156 |
+
{
|
| 157 |
+
"food": "pancakes",
|
| 158 |
+
"category": "breakfast",
|
| 159 |
+
"text": (
|
| 160 |
+
"Traditional pancakes are made with white flour and topped with syrup, creating a blood sugar spike. "
|
| 161 |
+
"Healthier version: blend one banana with two eggs for a 2-ingredient pancake, or use oat flour. "
|
| 162 |
+
"Top with fresh berries, a drizzle of pure maple syrup (less than regular syrup), "
|
| 163 |
+
"and a tablespoon of almond butter for protein and healthy fats. Banana-egg pancakes "
|
| 164 |
+
"have about 150 calories per serving with 10g protein."
|
| 165 |
+
),
|
| 166 |
+
},
|
| 167 |
+
{
|
| 168 |
+
"food": "french toast",
|
| 169 |
+
"category": "breakfast",
|
| 170 |
+
"text": (
|
| 171 |
+
"Make french toast healthier by using whole-grain or sourdough bread instead of white bread. "
|
| 172 |
+
"Use an egg wash with unsweetened almond milk and cinnamon instead of heavy cream. "
|
| 173 |
+
"Cook in coconut oil spray rather than butter. Top with Greek yogurt and fresh fruit "
|
| 174 |
+
"instead of powdered sugar and syrup. This version provides 250 calories per serving "
|
| 175 |
+
"with 15g protein from the egg and yogurt."
|
| 176 |
+
),
|
| 177 |
+
},
|
| 178 |
+
{
|
| 179 |
+
"food": "omelette",
|
| 180 |
+
"category": "breakfast",
|
| 181 |
+
"text": (
|
| 182 |
+
"Omelettes are already a great protein source. Make them healthier by using one whole egg "
|
| 183 |
+
"plus two egg whites to reduce cholesterol. Fill with spinach, tomatoes, mushrooms, and "
|
| 184 |
+
"bell peppers instead of just cheese and ham. Use feta or goat cheese (stronger flavor, "
|
| 185 |
+
"so you need less). Cook in olive oil instead of butter. A veggie omelette with feta "
|
| 186 |
+
"has about 250 calories and 20g protein."
|
| 187 |
+
),
|
| 188 |
+
},
|
| 189 |
+
# --- Mexican & Latin ---
|
| 190 |
+
{
|
| 191 |
+
"food": "tacos",
|
| 192 |
+
"category": "mexican",
|
| 193 |
+
"text": (
|
| 194 |
+
"Swap fried taco shells for soft corn tortillas, which are lower in calories and gluten-free. "
|
| 195 |
+
"Use grilled fish (mahi-mahi or shrimp) or seasoned black beans instead of ground beef. "
|
| 196 |
+
"Top with fresh pico de gallo, shredded cabbage, and a squeeze of lime instead of sour cream "
|
| 197 |
+
"and shredded cheese. Fish tacos with corn tortillas have about 200 calories each "
|
| 198 |
+
"with 20g protein and omega-3 fatty acids."
|
| 199 |
+
),
|
| 200 |
+
},
|
| 201 |
+
{
|
| 202 |
+
"food": "burrito",
|
| 203 |
+
"category": "mexican",
|
| 204 |
+
"text": (
|
| 205 |
+
"Burritos are calorie-dense (800+ calories) due to large tortillas, rice, cheese, and sour cream. "
|
| 206 |
+
"Make a burrito bowl instead: skip the tortilla and serve over cauliflower rice or a small portion "
|
| 207 |
+
"of brown rice. Use chicken or carnitas, black beans, corn salsa, and guacamole. "
|
| 208 |
+
"A burrito bowl with lean protein runs about 450 calories with 30g protein and plenty of fiber."
|
| 209 |
+
),
|
| 210 |
+
},
|
| 211 |
+
{
|
| 212 |
+
"food": "nachos",
|
| 213 |
+
"category": "mexican",
|
| 214 |
+
"text": (
|
| 215 |
+
"Replace tortilla chips with baked sweet potato rounds or baked whole-grain pita chips. "
|
| 216 |
+
"Use black bean dip instead of processed cheese sauce. Top with grilled chicken, "
|
| 217 |
+
"fresh jalapeños, diced tomatoes, and a dollop of Greek yogurt instead of sour cream. "
|
| 218 |
+
"Sweet potato nachos have about 300 calories per serving compared to 500+ for loaded nachos."
|
| 219 |
+
),
|
| 220 |
+
},
|
| 221 |
+
# --- Seafood ---
|
| 222 |
+
{
|
| 223 |
+
"food": "fish and chips",
|
| 224 |
+
"category": "seafood",
|
| 225 |
+
"text": (
|
| 226 |
+
"Traditional fish and chips are deep-fried, adding 300+ unnecessary calories. "
|
| 227 |
+
"Bake the fish instead: coat cod or haddock in a mixture of panko breadcrumbs, "
|
| 228 |
+
"lemon zest, and herbs, then bake at 400°F for 15 minutes. Make oven-baked fries "
|
| 229 |
+
"by tossing potato wedges with a tablespoon of olive oil and baking until crispy. "
|
| 230 |
+
"Baked fish and chips has about 350 calories compared to 800+ for the fried version."
|
| 231 |
+
),
|
| 232 |
+
},
|
| 233 |
+
{
|
| 234 |
+
"food": "shrimp",
|
| 235 |
+
"category": "seafood",
|
| 236 |
+
"text": (
|
| 237 |
+
"Shrimp is naturally low-calorie (about 7 calories per shrimp) and high in protein. "
|
| 238 |
+
"Avoid breaded and fried preparations. Instead, grill or sauté with garlic and olive oil. "
|
| 239 |
+
"Pair with roasted vegetables and quinoa for a complete meal. Shrimp is also rich in "
|
| 240 |
+
"selenium and vitamin B12. A grilled shrimp bowl with vegetables and quinoa "
|
| 241 |
+
"has about 350 calories and 35g protein."
|
| 242 |
+
),
|
| 243 |
+
},
|
| 244 |
+
# --- Desserts & Sweets ---
|
| 245 |
+
{
|
| 246 |
+
"food": "ice cream",
|
| 247 |
+
"category": "desserts",
|
| 248 |
+
"text": (
|
| 249 |
+
"Blend frozen bananas into 'nice cream' for a creamy, dairy-free alternative with no added sugar. "
|
| 250 |
+
"Add cocoa powder for chocolate flavor, or frozen berries for fruit varieties. "
|
| 251 |
+
"For a protein boost, blend in a tablespoon of peanut butter or protein powder. "
|
| 252 |
+
"Banana nice cream has about 100 calories per serving compared to 250+ for regular ice cream, "
|
| 253 |
+
"plus it provides potassium and fiber."
|
| 254 |
+
),
|
| 255 |
+
},
|
| 256 |
+
{
|
| 257 |
+
"food": "cake",
|
| 258 |
+
"category": "desserts",
|
| 259 |
+
"text": (
|
| 260 |
+
"Healthier cake alternatives: use almond flour or oat flour instead of white flour for more "
|
| 261 |
+
"protein and fiber. Replace butter with applesauce or mashed banana (cuts fat by 75%). "
|
| 262 |
+
"Use honey or maple syrup instead of refined sugar. Greek yogurt frosting (Greek yogurt "
|
| 263 |
+
"whipped with a touch of honey and vanilla) replaces buttercream. An almond flour cake "
|
| 264 |
+
"has about 180 calories per slice compared to 350+ for traditional cake."
|
| 265 |
+
),
|
| 266 |
+
},
|
| 267 |
+
{
|
| 268 |
+
"food": "chocolate",
|
| 269 |
+
"category": "desserts",
|
| 270 |
+
"text": (
|
| 271 |
+
"Choose dark chocolate (70% cacao or higher) over milk chocolate for more antioxidants "
|
| 272 |
+
"and less sugar. Dark chocolate contains flavonoids that support heart health. "
|
| 273 |
+
"Limit portions to 1-2 squares (about 1 oz). Pair with almonds or strawberries "
|
| 274 |
+
"for a satisfying, nutrient-rich dessert. An ounce of dark chocolate has about "
|
| 275 |
+
"170 calories with beneficial polyphenols and magnesium."
|
| 276 |
+
),
|
| 277 |
+
},
|
| 278 |
+
{
|
| 279 |
+
"food": "cookies",
|
| 280 |
+
"category": "desserts",
|
| 281 |
+
"text": (
|
| 282 |
+
"Healthy cookie recipe: mix 2 ripe bananas, 1 cup oats, and a handful of dark chocolate chips. "
|
| 283 |
+
"Optional add-ins: chia seeds, shredded coconut, or walnuts. Drop spoonfuls onto a baking sheet "
|
| 284 |
+
"and bake at 350°F for 12 minutes. These 3-ingredient cookies have about 60 calories each "
|
| 285 |
+
"with no added sugar, flour, or butter. The oats provide fiber and the banana adds natural sweetness."
|
| 286 |
+
),
|
| 287 |
+
},
|
| 288 |
+
# --- Chicken & Poultry ---
|
| 289 |
+
{
|
| 290 |
+
"food": "fried chicken",
|
| 291 |
+
"category": "poultry",
|
| 292 |
+
"text": (
|
| 293 |
+
"Fried chicken absorbs significant oil during cooking, adding 200+ calories per piece. "
|
| 294 |
+
"Oven-fried alternative: marinate chicken thighs in buttermilk, coat in seasoned whole-wheat "
|
| 295 |
+
"panko, and bake at 425°F for 25 minutes, flipping halfway. Use skinless chicken to reduce fat. "
|
| 296 |
+
"Air-frying is another excellent option. Oven-fried chicken has about 220 calories per piece "
|
| 297 |
+
"compared to 400+ for deep-fried, with the same satisfying crunch."
|
| 298 |
+
),
|
| 299 |
+
},
|
| 300 |
+
{
|
| 301 |
+
"food": "chicken wings",
|
| 302 |
+
"category": "poultry",
|
| 303 |
+
"text": (
|
| 304 |
+
"Buffalo wings are typically deep-fried and coated in butter-based sauce. Healthier version: "
|
| 305 |
+
"bake wings at 400°F for 40 minutes until crispy, then toss in a sauce made from hot sauce "
|
| 306 |
+
"and a small amount of honey instead of butter. Serve with carrot and celery sticks with "
|
| 307 |
+
"Greek yogurt ranch dip. Baked wings have about 80 calories each versus 120+ for fried."
|
| 308 |
+
),
|
| 309 |
+
},
|
| 310 |
+
# --- Rice & Grains ---
|
| 311 |
+
{
|
| 312 |
+
"food": "white rice",
|
| 313 |
+
"category": "grains",
|
| 314 |
+
"text": (
|
| 315 |
+
"White rice is a high-glycemic food with limited nutrients. Healthier alternatives: "
|
| 316 |
+
"brown rice (more fiber and B vitamins), quinoa (complete protein with all 9 essential amino acids), "
|
| 317 |
+
"cauliflower rice (90% fewer calories), or farro (chewy texture with high fiber). "
|
| 318 |
+
"Mixing half cauliflower rice with half regular rice is an easy way to cut calories by 45% "
|
| 319 |
+
"while maintaining a familiar texture."
|
| 320 |
+
),
|
| 321 |
+
},
|
| 322 |
+
# --- Soups ---
|
| 323 |
+
{
|
| 324 |
+
"food": "soup",
|
| 325 |
+
"category": "soups",
|
| 326 |
+
"text": (
|
| 327 |
+
"Cream-based soups (chowder, bisque) can have 300+ calories per bowl. Switch to broth-based "
|
| 328 |
+
"soups with plenty of vegetables, beans, and lean protein. Blend a portion of the vegetables "
|
| 329 |
+
"to create creaminess without actual cream. Lentil soup is exceptionally nutritious: "
|
| 330 |
+
"one bowl has about 230 calories, 18g protein, and 15g fiber. Season with turmeric, "
|
| 331 |
+
"cumin, and lemon for anti-inflammatory benefits."
|
| 332 |
+
),
|
| 333 |
+
},
|
| 334 |
+
# --- Snacks ---
|
| 335 |
+
{
|
| 336 |
+
"food": "chips",
|
| 337 |
+
"category": "snacks",
|
| 338 |
+
"text": (
|
| 339 |
+
"Potato chips are deep-fried and high in sodium. Healthier alternatives: bake thin-sliced "
|
| 340 |
+
"sweet potatoes or kale leaves with a light olive oil spray and sea salt at 300°F. "
|
| 341 |
+
"Air-popped popcorn seasoned with nutritional yeast is another excellent swap — "
|
| 342 |
+
"3 cups have only 90 calories with fiber. Roasted chickpeas (tossed with spices and "
|
| 343 |
+
"baked at 400°F for 30 minutes) provide protein and crunch at 120 calories per serving."
|
| 344 |
+
),
|
| 345 |
+
},
|
| 346 |
+
{
|
| 347 |
+
"food": "french fries",
|
| 348 |
+
"category": "snacks",
|
| 349 |
+
"text": (
|
| 350 |
+
"French fries absorb a lot of oil during deep-frying. Healthier swaps: bake sweet potato fries "
|
| 351 |
+
"(more vitamin A and fiber) or regular potato wedges tossed in one tablespoon of olive oil "
|
| 352 |
+
"at 425°F for 25 minutes. Air-fried fries use 80% less oil. Jicama fries are a low-carb "
|
| 353 |
+
"alternative with a satisfying crunch. Baked sweet potato fries have about 130 calories "
|
| 354 |
+
"per serving compared to 365 for fast-food fries."
|
| 355 |
+
),
|
| 356 |
+
},
|
| 357 |
+
# --- Drinks ---
|
| 358 |
+
{
|
| 359 |
+
"food": "smoothie",
|
| 360 |
+
"category": "drinks",
|
| 361 |
+
"text": (
|
| 362 |
+
"Many commercial smoothies are sugar bombs (60-80g sugar). Build a balanced smoothie: "
|
| 363 |
+
"use unsweetened almond milk as the base, add one cup of frozen berries (natural sweetness), "
|
| 364 |
+
"a handful of spinach (undetectable flavor, adds iron and vitamins), one tablespoon of "
|
| 365 |
+
"nut butter or chia seeds (healthy fats and protein), and half a banana for creaminess. "
|
| 366 |
+
"This balanced smoothie has about 250 calories with 10g protein and 8g fiber."
|
| 367 |
+
),
|
| 368 |
+
},
|
| 369 |
+
# --- General Nutrition Principles ---
|
| 370 |
+
{
|
| 371 |
+
"food": "general",
|
| 372 |
+
"category": "nutrition",
|
| 373 |
+
"text": (
|
| 374 |
+
"The plate method is an easy way to build balanced meals: fill half your plate with "
|
| 375 |
+
"non-starchy vegetables (leafy greens, broccoli, peppers), one quarter with lean protein "
|
| 376 |
+
"(chicken, fish, tofu, legumes), and one quarter with whole grains or starchy vegetables. "
|
| 377 |
+
"This approach naturally controls portions and ensures nutrient diversity without counting calories."
|
| 378 |
+
),
|
| 379 |
+
},
|
| 380 |
+
{
|
| 381 |
+
"food": "general",
|
| 382 |
+
"category": "nutrition",
|
| 383 |
+
"text": (
|
| 384 |
+
"Healthy cooking methods ranked by calorie impact: steaming and poaching add zero calories; "
|
| 385 |
+
"grilling and roasting need minimal oil; sautéing uses moderate oil; deep-frying adds the most. "
|
| 386 |
+
"Switching from frying to baking typically saves 100-300 calories per serving. "
|
| 387 |
+
"Use non-stick pans, cooking sprays, or small amounts of high-quality olive or avocado oil."
|
| 388 |
+
),
|
| 389 |
+
},
|
| 390 |
+
{
|
| 391 |
+
"food": "general",
|
| 392 |
+
"category": "nutrition",
|
| 393 |
+
"text": (
|
| 394 |
+
"Common healthy ingredient swaps: Greek yogurt for sour cream (more protein, less fat), "
|
| 395 |
+
"avocado for mayo (healthy monounsaturated fats), coconut aminos for soy sauce (less sodium), "
|
| 396 |
+
"nutritional yeast for cheese (B vitamins, lower calories), date paste for refined sugar "
|
| 397 |
+
"(adds fiber and minerals), and lettuce wraps for tortillas (saves 100+ calories per wrap)."
|
| 398 |
+
),
|
| 399 |
+
},
|
| 400 |
+
{
|
| 401 |
+
"food": "general",
|
| 402 |
+
"category": "nutrition",
|
| 403 |
+
"text": (
|
| 404 |
+
"Protein is the most satiating macronutrient, helping you feel full longer. "
|
| 405 |
+
"Aim for 20-30g protein per meal. Best lean protein sources: chicken breast (31g per 4 oz), "
|
| 406 |
+
"Greek yogurt (17g per cup), lentils (18g per cup cooked), eggs (6g each), "
|
| 407 |
+
"salmon (25g per 4 oz with heart-healthy omega-3s), and tofu (20g per cup)."
|
| 408 |
+
),
|
| 409 |
+
},
|
| 410 |
+
{
|
| 411 |
+
"food": "general",
|
| 412 |
+
"category": "nutrition",
|
| 413 |
+
"text": (
|
| 414 |
+
"Fiber is crucial for digestive health and satiety but most people only get half "
|
| 415 |
+
"the recommended 25-30g per day. High-fiber foods: lentils (15g per cup), "
|
| 416 |
+
"black beans (15g per cup), avocado (10g each), raspberries (8g per cup), "
|
| 417 |
+
"chia seeds (10g per ounce), and oats (4g per cup cooked). Adding these to meals "
|
| 418 |
+
"increases fullness and supports healthy blood sugar levels."
|
| 419 |
+
),
|
| 420 |
+
},
|
| 421 |
+
# --- Indian ---
|
| 422 |
+
{
|
| 423 |
+
"food": "curry",
|
| 424 |
+
"category": "indian",
|
| 425 |
+
"text": (
|
| 426 |
+
"Many curries use heavy cream and ghee, pushing calories to 500+ per serving. "
|
| 427 |
+
"Healthier version: use coconut milk (light variety) or blended cashews for creaminess. "
|
| 428 |
+
"Load with vegetables like cauliflower, chickpeas, and spinach. Spices like turmeric, "
|
| 429 |
+
"cumin, and ginger have anti-inflammatory properties. A chickpea spinach curry "
|
| 430 |
+
"with light coconut milk has about 280 calories per serving with 15g protein and 10g fiber."
|
| 431 |
+
),
|
| 432 |
+
},
|
| 433 |
+
{
|
| 434 |
+
"food": "biryani",
|
| 435 |
+
"category": "indian",
|
| 436 |
+
"text": (
|
| 437 |
+
"Biryani is calorie-dense due to generous amounts of oil, white rice, and sometimes fried onions. "
|
| 438 |
+
"Lighter version: use brown basmati rice, reduce oil to 1 tablespoon, and increase vegetables. "
|
| 439 |
+
"Add lean protein like chicken breast or chickpeas. Use yogurt-based marinade for flavor "
|
| 440 |
+
"without excess fat. A lighter chicken biryani has about 350 calories per serving "
|
| 441 |
+
"compared to 550+ for the traditional version."
|
| 442 |
+
),
|
| 443 |
+
},
|
| 444 |
+
# --- Steak & Red Meat ---
|
| 445 |
+
{
|
| 446 |
+
"food": "steak",
|
| 447 |
+
"category": "meat",
|
| 448 |
+
"text": (
|
| 449 |
+
"Choose lean cuts like sirloin, flank, or tenderloin over ribeye or T-bone to reduce "
|
| 450 |
+
"saturated fat. Trim visible fat before cooking. Grill or broil instead of pan-frying in butter. "
|
| 451 |
+
"Limit portion to 4-6 oz (about the size of a deck of cards). Pair with roasted vegetables "
|
| 452 |
+
"and a baked sweet potato instead of creamy sides. A 4 oz grilled sirloin has about "
|
| 453 |
+
"200 calories and 26g protein."
|
| 454 |
+
),
|
| 455 |
+
},
|
| 456 |
+
# --- Breakfast cereals ---
|
| 457 |
+
{
|
| 458 |
+
"food": "cereal",
|
| 459 |
+
"category": "breakfast",
|
| 460 |
+
"text": (
|
| 461 |
+
"Most commercial cereals are high in added sugar (10-15g per serving). Healthier alternatives: "
|
| 462 |
+
"steel-cut oats (more filling, lower glycemic), overnight oats with chia seeds and berries, "
|
| 463 |
+
"or homemade granola with oats, nuts, and a small amount of honey. If buying cereal, "
|
| 464 |
+
"choose options with less than 5g sugar and at least 3g fiber per serving. "
|
| 465 |
+
"Top with fresh fruit instead of adding sugar."
|
| 466 |
+
),
|
| 467 |
+
},
|
| 468 |
+
{
|
| 469 |
+
"food": "waffles",
|
| 470 |
+
"category": "breakfast",
|
| 471 |
+
"text": (
|
| 472 |
+
"Traditional waffles use white flour, sugar, and butter. Make protein waffles by blending "
|
| 473 |
+
"oats, banana, eggs, and a scoop of protein powder. The result is crispy outside, fluffy inside, "
|
| 474 |
+
"with about 200 calories and 20g protein per waffle. Top with Greek yogurt and fresh berries "
|
| 475 |
+
"instead of syrup and whipped cream for a balanced breakfast."
|
| 476 |
+
),
|
| 477 |
+
},
|
| 478 |
+
# --- Mediterranean ---
|
| 479 |
+
{
|
| 480 |
+
"food": "falafel",
|
| 481 |
+
"category": "mediterranean",
|
| 482 |
+
"text": (
|
| 483 |
+
"Traditional falafel is deep-fried, absorbing significant oil. Baked falafel version: "
|
| 484 |
+
"blend chickpeas with parsley, cilantro, garlic, cumin, and a tablespoon of olive oil. "
|
| 485 |
+
"Form into balls and bake at 375°F for 25 minutes, flipping halfway. Serve in whole-wheat pita "
|
| 486 |
+
"with tahini, cucumber, and tomato. Baked falafel has about 60 calories per ball "
|
| 487 |
+
"compared to 100+ for deep-fried."
|
| 488 |
+
),
|
| 489 |
+
},
|
| 490 |
+
{
|
| 491 |
+
"food": "hummus",
|
| 492 |
+
"category": "mediterranean",
|
| 493 |
+
"text": (
|
| 494 |
+
"Hummus is already a healthy snack — chickpeas provide protein and fiber, and tahini offers "
|
| 495 |
+
"healthy fats. To make it even healthier, use less tahini and more lemon juice, or add "
|
| 496 |
+
"roasted beets, roasted red pepper, or edamame as a base for variety and extra nutrients. "
|
| 497 |
+
"Pair with raw vegetables instead of pita chips. A quarter cup of hummus has about "
|
| 498 |
+
"100 calories with 5g protein and 3g fiber."
|
| 499 |
+
),
|
| 500 |
+
},
|
| 501 |
+
# --- Chinese ---
|
| 502 |
+
{
|
| 503 |
+
"food": "sweet and sour chicken",
|
| 504 |
+
"category": "chinese",
|
| 505 |
+
"text": (
|
| 506 |
+
"Sweet and sour chicken is typically battered, deep-fried, and coated in sugary sauce (600+ calories). "
|
| 507 |
+
"Healthier version: use baked chicken breast pieces, and make sauce from rice vinegar, "
|
| 508 |
+
"a tablespoon of honey, tomato paste, and low-sodium soy sauce. Add pineapple chunks "
|
| 509 |
+
"and bell peppers. Serve over cauliflower rice. This version has about 300 calories "
|
| 510 |
+
"with 30g protein."
|
| 511 |
+
),
|
| 512 |
+
},
|
| 513 |
+
{
|
| 514 |
+
"food": "lo mein",
|
| 515 |
+
"category": "chinese",
|
| 516 |
+
"text": (
|
| 517 |
+
"Lo mein noodles are stir-fried with oil, adding significant calories. Swap for zucchini noodles "
|
| 518 |
+
"or shirataki noodles. Use less oil (1 tbsp sesame oil is enough for flavor), add extra vegetables "
|
| 519 |
+
"(snap peas, bok choy, mushrooms, carrots), and include lean protein like shrimp or tofu. "
|
| 520 |
+
"Use low-sodium soy sauce or coconut aminos. Veggie lo mein with shirataki noodles "
|
| 521 |
+
"has about 180 calories per serving."
|
| 522 |
+
),
|
| 523 |
+
},
|
| 524 |
+
# --- Baked Goods ---
|
| 525 |
+
{
|
| 526 |
+
"food": "muffin",
|
| 527 |
+
"category": "baked_goods",
|
| 528 |
+
"text": (
|
| 529 |
+
"Store-bought muffins can have 400-500 calories and 30g+ sugar. Healthy muffin recipe: "
|
| 530 |
+
"mix 2 cups oat flour, 2 mashed bananas, 2 eggs, 1/4 cup honey, 1 cup blueberries. "
|
| 531 |
+
"Bake at 350°F for 18 minutes. Each muffin has about 120 calories with natural sweetness "
|
| 532 |
+
"from banana and berries. Add walnuts for omega-3 fatty acids."
|
| 533 |
+
),
|
| 534 |
+
},
|
| 535 |
+
{
|
| 536 |
+
"food": "bread",
|
| 537 |
+
"category": "baked_goods",
|
| 538 |
+
"text": (
|
| 539 |
+
"Choose bread wisely: whole-grain or sprouted-grain bread (like Ezekiel) has more fiber, "
|
| 540 |
+
"protein, and nutrients than white bread. Sourdough has a lower glycemic index due to "
|
| 541 |
+
"fermentation. Avoid breads with high-fructose corn syrup or long ingredient lists. "
|
| 542 |
+
"Two slices of sprouted-grain bread have about 160 calories with 8g protein and 6g fiber."
|
| 543 |
+
),
|
| 544 |
+
},
|
| 545 |
+
]
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit>=1.40.0
|
| 2 |
+
chromadb>=0.5.0
|
| 3 |
+
transformers>=4.40.0
|
| 4 |
+
torch
|
| 5 |
+
Pillow>=10.0
|
| 6 |
+
google-genai>=1.0.0
|
| 7 |
+
pydantic-settings>=2.0
|