Author: Andrei Florescu
This project aims to develop an interactive meal planner where users can achieve their dietary goals by generating personalized meal plans based on their preferences, as well as dietary restrictions and nutritional goals. The targeted users for this projects are home cooks, fitness enthusiasts, and individuals with specific dietary needs who are looking for fast meal planning ideas that align with their health goals.
The data used in this projects comes from the dataset available on github. The original source of this dataset was created by scraping from the Epicurious Website. The content was uploaded to Kaggle as Food Ingredients and Recipes Dataset with Images. For the nutritional values the openfoodfacts API is used. For other information gathered the Serper.Dev Tool is used to scrape information from different google search queries.
The project contains 3 applications:
- Backend(mealplannercrew): a crew and api application
- Frontend(meal-planner-frontend): a vite react web application
- Backend without generative AI(meal_api_without_ai): a simple backend that generates meals based on inputs on a form
Backend(mealplannercrew)
The project contains an env.example file which contains an example of how the .env file should look like and what it needs to contain in order to run.
This project contains three parts:
API app.py
The API part of the app is created using the fastapi library. This projects parts are hardcoded for the moment (database path, app url, cors, etc.). Currently the api is served at the default port 8000. The API uses the database service and the crew in order to provide the user with meal information. The crew is run asynchronously on a different thread in order to not block the API. The user can check the status of his requests at any given point to see if they are completed. The API contains the following endpoints:
- GET /users - returns the list of users and their information
- GET /status/{username} - returns the list of requests the user has made to the crew and their status as well as results
- POST /generate-plan - (username: str, prompt: str) starts the generation of a meal or a plan depending on the user prompt usint the crew
- POST /users/profile - (profile: UserProfile) saves a new user to the database which can further be used to make requests to the crew
- GET /docs - view the API Swagger page
The api can be run locally using the following command:
uv run uvicorn app:app --env-file .envDatabase service database-service
The project uses an SQLite database to store the users and the result of the requests done by the users to the crew. The database service contains the following features:
- init_db: creates a SQLite Database and its tables if it doesn't exist and creates a connection to it.
- save_user_profile: saves a new user
- get_user: returns a user by their username
- get_all_users: returns a list of all users
- save_task_result: saves the result of a prompt done by a user to the crew, as well as when the request was done and its current status (pending, success, fail)
- get_task_result: gets the contents of a task requested by a user
- get_user_tasks: gets a list of all the requests done by the user to the crew
This part needs to be developed further to accomodate update and delete operations.
The generative AI logic of the project is done by using crewai. The crew can be run using the api or individually by running the commands described in the pyproject.toml such as crewai run
The crew uses the following LLMs:
- groq/llama-3.3-70b-versatile
- groq/meta-llama/llama-4-scout-17b-16e-instruct
- mistral/mistral-small-latest
- mistral/mistral-medium-latest
- mistral/mistral-large-latest
The reason for including this many LLMs is to split the tasks of the crew between smarter, faster or more balanced LLMs to increase efficiency as well as use the free plan of the groq and mistral services in full, and bypass quota limitations.
To use the LLMs from their respective APIs, the .env.example needs to be copied as an .env file and have each API key filled out in order for the crew to work properly.
There are 5 agents (1 commented out) described in the crew configuration:
- recipe_researcher - Finds the best meals from the recipe database that match the user's preferences.
- nutritionist - Calculates the exact nutritional breakdown for a given meal
- cost_estimator - Find the most current market prices for the ingredients and calculate the meal cost
- plan_summarizer - Synthesizes the meal choices, nutritional data, and cost estimates
- creative_chef (commented out) - used to create new recipes that don't appear in the dataset, currently excluded because it generated too many tokens and caused LLM errors in the crew.
Each agent uses an LLM according to their needs, for example the retrieval task of the researcher uses a fast LLM while the plan summarizer uses a smarter LLM with more params to generate a more accurate and appealing plan. Currently all the agents have one task according to their description. The agent definitions can be found here and the task definitions can be found here.
The result of the crew is currently saved in the database and as a markdown file.
The custom tools are created in order for the agents to be able to complete their tasks. The following tools were added:
- recipe_tool: user by the recipe researcher to search in the recipe dataset csv; initializes itself before the crew is run to create embeddings of the dataset and cache them to disk.
- macronutrients_tool: uses the openfoodfacts API to get ingredient macros; used by the nutritionist
- macronutrient_tool_online: uses the Serper.Dev Tool to search for an ingredient macronutrients in case the other macronutrient tool returned no results; used by the nutritionist
- price_tool: uses the Serper.Dev Tool to search for an ingredients price per 100g in Euro; used by the cost estimator
- body_composition_energy: a tool used by the plan summarizer to calculate the BMI of a person if they include that user information in their profile and their maintenance calories as well as weight loss/gain calories.
The tools can be found in this folder
Frontend(meal-planner-frontend)
The frontend application is a vite React app which currently is created as a one file (App.jsx) frontend app. It can be run using the npm run dev command and it's served by default on the port 5173. It contains the following sections:
- User profile creation
- Generate plan
- View users
- View user requests to the crew
The frontend application calls the Generative AI API in order to retrieve information from the database and display it.
Backend without Generative AI(meal_api_without_ai)
An API application used for comparison to the Generative AI API app. This is not included in any docker definition and can be run only locally. To run it you need to install its requirements into a python environment and the run it using the following command: python -m main. The API is served on the default port 8000 which conflicts with the other API, so only one of them can be run at a time.
The api has the following endpoints:
- GET /generate-meal - (params: max_calories, alergies, dietary pref) generate one meal by querying the database using a simple meal service and tries to match the meal to the parameters given by the user and then get its nutritional and price data by using the openfoodfacts API. At the end the result is generated as a hardcoded markdown.
- GET /docs - view the API Swagger page
To run the Generative AI Backend and the associated Frontend up the following steps need to be completed:
Copy the .env.example from the root folder as .env and fill the variables with API Keys for each service.
Make sure to have a running docker instance and then from the root folder run the following command
docker-compose up --build -d You can choose wether to attach or detach from the terminal by using the -d parameter
This command will create two containers and serve them by default at the port 8000 for the backend app and port 5173 for the frontend app on the localhost.
To run the project locally a conda environment is needed. The definition of the environment can be found in the yaml file from the root folder which references the requirements file.
conda env create -f environment.ymlTo run the backend locally you need to cd into the directory and run
uv sync
crewai run # run without API
uv run uvicorn app:app --env-file .env # run with APITo run the frontend locally you need to cd into the directory and run
npm install
npm run devTo run the backend without generative AI you need to cd into the directory and run
pip install -r requirements.txt
pyhton -m mainIn the folder result_examples a few example runs can be found to showcase each API's capabilities. The runs are represented as markdown files. For the Generative AI API examples there are examples for one meal or for day plans and each time a different user is used with different dietary preferences, body compositions as well as nutritional goals. For the results without generative AI there are a few example runs of the other API saved as markdown which showcase that the API can retrieve meals, but it's not as easy to include user preferences and accurate live data into the results.