A smart nutrition assistant that helps you track calories and macros with minimal effort. Simply take a photo of your meal, and the app will recognize ingredients, estimate portions, and calculate nutritional values. Designed for people who want to maintain a healthy lifestyle without manual food logging.
π Before you start: We recommend reading the in-app guide (
How it works?in the sidebar) for detailed usage instructions, tips, and explanations of all features.
- πΈ Photo Recognition: Upload a photo of your meal β AI identifies ingredients and estimates weights using a local vision LLM
- π Multi-language Support: Ingredient names are available in English and Russian. The translation system is modular β just add new localization files or edit existing ones to extend support to other languages
- π Statistics & Analytics: View calorie/macro breakdowns, track weight changes, and compare nutrient intake against your personal norms over time using static matplotlib charts
- π― Personalized Goals: Set weight loss, maintenance, or gain targets with custom BMR calculations based on your profile
- π Private & Secure: All data stays on your server; authentication via JWT tokens
Perfect for fitness enthusiasts, dietitians, or anyone who wants a smarter way to track nutrition.
| Layer | Technology |
|---|---|
| Frontend | Streamlit 1.57.0 (Python-based reactive UI) |
| Backend | FastAPI 0.136.1 + Uvicorn (async application server) |
| Database | PostgreSQL 16 with pgvector (semantic search) |
| AI/ML | Sentence Transformers (embeddings), Ollama (vision LLM) |
| Auth | JWT (python-jose), bcrypt password hashing (passlib) |
| HTTP Clients | requests (sync), httpx (async) |
| Data Processing | numpy, matplotlib |
| Validation | pydantic (data validation and settings management) |
| Deployment | Docker + Docker Compose |
- Python: 3.11+ (tested on 3.12)
- Docker + Docker Compose (v2.0+)
- tmux (for local development with Makefile)
- Ollama running locally with a vision-capable model (e.g.,
qwen3.5:9b) - Git for cloning the repository
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 4 cores | 8+ cores |
| RAM | 8 GB | 16+ GB |
| GPU | None (CPU inference) | NVIDIA GPU with 12 GB VRAM (CUDA 12) |
| Storage | 10 GB free | 20+ GB SSD (for models + cache) |
π‘ Note: The application was tested on NVIDIA GPU with 12 GB VRAM (CUDA 12) using the
qwen3.5:9bmodel (~9B parameters).Performance reference:
- CPU inference: ~1.5 min cold start (Ollama load), ~20β40 seconds per image on warm start
- GPU inference: ~3β4Γ faster than CPU for both cold and warm runs
CPU-only mode is fully supported; GPU is recommended for smoother interactive experience.
git clone https://github.com/your-username/calorie-tracker.git
cd calorie-trackerCreate a .env file in the project root:
OLLAMA_HOST = http://172.22.224.1:11434
MODEL_NAME = qwen3.5:9b
SERVER_URL = http://127.0.0.1:8000
STREAMLIT_URL = http://localhost:8501
DB_CONFIG = '{"host": "localhost", "port": 5432, "database": "calorie_tracker_db", "user": "postgres", "password": "your_db_password"}'
DB_PASSWORD='your_secure_db_password'
SECRET_KEY = 'your_jwt_secret_key_here'
LOG_LEVEL = INFO
TOKEN_EXPIRE_MINUTES = 1440
ADMIN_PASSWORD = "your_admin_password_here"
β οΈ Important for WSL2/Docker Desktop:
OLLAMA_HOSTshould point to your WSL host IP (e.g.,172.22.224.1) to allow containers to reach OllamaDB_CONFIGmust be a valid JSON string β keep outer single quotes, double quotes inside JSON- Never commit
.envto version control
Place your nutrition dataset as nutrition.csv in the helper/ folder:
name,calories,protein,fats,carbohydrates
"Apple, raw",52,0.3,0.2,14
"Chicken breast",165,31,3.6,0You can use the provided sample file or replace it with your own dataset. If your CSV uses different column names, see the customization notes below.
docker compose up -d --buildThis will:
- Build and start PostgreSQL with pgvector extension
- Initialize the database schema
- Load ingredients from
nutrition.csv - Start the backend API
- Start the Streamlit frontend
- Generate and sync ingredient translations
- Frontend: http://localhost:8501
- Backend API docs: http://localhost:8000/docs
- Database: localhost:5432 (postgres/postgres)
# All services
docker compose logs -f
# Specific service
docker compose logs -f frontend
docker compose logs -f backendgit clone https://github.com/your-username/calorie-tracker.git
cd calorie-tracker
python -m venv .venv
source .venv/bin/activate # Linux/macOS
# .venv\Scripts\activate # Windows
pip install -r requirements-backend.txt
pip install -r requirements-frontend.txt# Using Docker (recommended):
docker run -d \
--name calorie-db \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=calorie_tracker_db \
-p 5432:5432 \
pgvector/pgvector:pg16docker exec -i calorie-db psql -U postgres -d calorie_tracker_db < backend/create_db.sqlUse the same format as shown in Step 2 above, adjusting DB_CONFIG and OLLAMA_HOST for localhost if needed.
# Load nutrition data
python helper/csv_to_db.py
# Generate translation dictionary
python helper/init_translation_dict.pyTerminal 1 β Backend:
cd backend
uvicorn service:app &Terminal 2 β Frontend:
cd frontend
streamlit run main_page.pycalorie-tracker/
βββ backend/
β βββ service.py # FastAPI app entry point
β βββ assistant.py # LLM client (Ollama integration)
β βββ auth.py # JWT authentication
β βββ db_connector.py # PostgreSQL asyncpg connector
β βββ schemas.py # Pydantic models for request/response validation
β βββ create_db.sql # Database schema initialization
β βββ Dockerfile # Backend container setup
β βββ prompt_*.txt # LLM system prompts (BMR, recognition, translation, macros)
β
βββ db/
β βββ Dockerfile # PostgreSQL + pgvector setup
β
βββ frontend/
β βββ main_page.py # Streamlit entry point (router)
β βββ menu.py # Navigation sidebar with "How it works?" guide
β βββ translator.py # Translation manager for ingredients
β βββ Dockerfile # Frontend container setup
β βββ .streamlit/
β β βββ config.toml # Streamlit configuration (theme, server settings)
β βββ pages/
β β βββ home.py # Main dashboard page after authentication
β β βββ recognition.py # Photo recognition interface
β β βββ daily_log.py # Meal logging interface
β β βββ settings.py # User profile and nutrition goals
β β βββ general_stat.py # Statistics and charts (matplotlib-based)
β β βββ register.py # User registration
β βββ handlers/
β β βββ api_handler.py # API request wrapper
β β βββ recognition_handler.py
β β βββ daily_log_handler.py
β β βββ home_handler.py
β β βββ settings_handler.py
β β βββ general_stat_handler.py
β β βββ register_handler.py
β β βββ nutrition_table.py # Table initializer and handler for ingredients
β β βββ main_page_handler.py
β β βββ init_session_state.py # Initializes Streamlit session_state for persistent UI data
β βββ resources/
β βββ locales/
β β βββ en.yaml # English UI translations
β β βββ ru.yaml # Russian UI translations
β β βββ ingredient_translations.json # Ingredient name translations (EN β RU)
β βββ icons8-chinese-noodle-100.png
β
βββ helper/
β βββ csv_to_db.py # Initializes basic ingredients in DB + generates semantic embeddings
β βββ init_translation_dict.py # Generates base translation JSON from CSV
β βββ pre_run_sync.py # Startup script: generates base translations and syncs with backend
β βββ nutrition.csv # Nutrition dataset (sample or custom)
β
βββ docker-compose.yml # Multi-container orchestration
βββ requirements-backend.txt # Backend dependencies
βββ requirements-frontend.txt # Frontend dependencies
βββ .env # Environment variables (git-ignored)
βββ .gitignore # Git ignore rules
βββ .dockerignore # Docker ignore rules
βββ Makefile # Quick start commands for local development
βββ LICENSE # Project license
βββ README.md # This file
If your nutrition.csv uses different column headers, edit the fields_config dictionary in both scripts:
In helper/csv_to_db.py:
fields_config = {
"name": "name", # Column with ingredient names
"calories": "calories", # Column with calorie values
"fats": "total_fat", # Column with fat values
"proteins": "protein", # Column with protein values
"carbohydrates": "carbohydrate", # Column with carb values
}In helper/init_translation_dict.py:
# Change this parameter when calling the function:
generate_base_translations_from_csv(
csv_path="nutrition.csv",
col_with_ing_name="your_column_name", # π Your column with ingredient names
# ... other params
)The project includes a Makefile for quick local development using tmux (requires tmux installed):
# Start backend and frontend in detached tmux sessions
make run_app
# View live output from both sessions without attaching
make peek
# Stop both sessions
make stopπ‘ Note: The
run_appcommand automatically handles session cleanup, starts the backend (uvicorn service:app --reload) and frontend (streamlit run main_page.py) in separatetmuxwindows, and prints quick reference commands. Requirestmuxto be installed on your system.
| Issue | Solution |
|---|---|
pgvector extension does not exist |
Use pgvector/pgvector:pg16 Docker image or run CREATE EXTENSION vector; manually |
| Ollama connection refused | Verify OLLAMA_HOST points to correct IP; run ollama serve and ollama pull qwen3.5:9b |
| Missing translations | Run python helper/init_translation_dict.py or check pre_run_sync.py logs |
| Slow semantic search | Ensure pgvector index is built; pre-generate embeddings via csv_to_db.py |
| JWT auth fails | Check SECRET_KEY matches; verify token expiration in TOKEN_EXPIRE_MINUTES |
| Database connection failed | Ensure Docker container is running (docker ps) and DB_CONFIG matches container port/user/password |
| CSV columns not found | Edit fields_config in helper/csv_to_db.py AND col_with_ing_name in init_translation_dict.py |
| First-run sync fails | Check if backend is running. If not, restart all containers: docker compose down && docker compose up -d --build. Default admin password is admin. |
See LICENSE in this repository.
π‘ Tip: For the best experience, start with a small
nutrition.csv(100β500 items) to test the pipeline, then scale up. Embedding generation for 10k items takes ~5β10 minutes on CPU, ~1 minute on GPU.
Built with β€οΈ for smarter nutrition tracking.