Skip to content

BustosAndrew/lahacks

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Chef.ai

AI recipe generator web app all made in Python using the reflex framework and Gemini API!

Deployment

Check it out!

Screenshot 2024-04-30 at 10 57 23 PM

Installation

git clone https://github.com/BustosAndrew/lahacks.git
cd lahacks
pip install -r requirements.txt
reflex run

Code Examples

import google.generativeai as genai
import os
genai.configure(api_key=os.environ.get("GEMINI_API_KEY"))
# Set up the model
generation_config = {
"temperature": 0.9,
"top_p": 0.95,
"top_k": 32,
"max_output_tokens": 1024,
}
safety_settings = [
{
"category": "HARM_CATEGORY_HARASSMENT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
]
model = genai.GenerativeModel(model_name="gemini-1.5-pro-latest",
generation_config=generation_config,
safety_settings=safety_settings)
def generate_recipe(req: dict):
print(req["cookware"])
prompt_parts = [
"Provide a healthier recipe consistent with the provided ingredients, along with the steps to make them. Adjust the entire recipe depending on the provided cookware. Provide nutritional info where possible. Add a name for the recipe and categorize the details as either healthy or unhealthy. Make sure to flag anything as unhealthy from the ingredients.",
"Ingredients: " + req["ingredients"],
"Use only these ingredients (True/False): " +
str(req["onlyIngredients"]),
"Cookware: " + req["cookware"],
]
response = model.generate_content(prompt_parts, stream=True)
for chunk in response:
yield chunk
def generate_prompt(req: str):
prompt_parts = [
"Generate only one prompt of an image that resembles very closely to the end result of the provided recipe. Be as specific and detailed as possible according to the recipe guideline. Ensure that the prompt also adheres closely to the given amount per ingredient. Only give the plain text of the prompt idea without any formatting or headers.",
"Recipe details: " + req,
]
response = model.generate_content(prompt_parts)
return response.text

import reflex as rx
from lahacks.states.field_state import FieldState
from lahacks.api.gemini import generate_recipe, generate_prompt
from lahacks.pages.text2img import text2img
class DynamicFormState(rx.State):
form_data: dict = {}
form_fields: list[list[str]] = []
ai_response: str = ""
submitted: bool = False
cantSubmit: bool = True
buttonText: str = "Submit"
imageLink: str = ""
onlyIngredients: bool = False
def set_only_ingredients(self, value: bool):
self.onlyIngredients = value
def add_field(self, ingredient: str, quantity: int, unit: str):
if not ingredient:
return
self.form_fields.append([ingredient, str(quantity), unit])
self.cantSubmit = False
def handle_submit(self, form_data: dict):
self.submitted = True
self.cantSubmit = True
self.buttonText = "Generating..."
yield
if not self.form_fields:
self.submitted = False
self.cantSubmit = False
self.buttonText = "Submit"
return
self.ai_response = ""
self.form_data = form_data
chunks = generate_recipe({
"ingredients": ", ".join([
f"{field[1]}{field[2]} {field[0]}"
for field in self.form_fields
]),
"onlyIngredients": self.onlyIngredients,
"cookware": form_data.get("cookware", "Decide based on the recipe."),
})
for chunk in chunks:
self.ai_response += chunk.text
yield
if self.ai_response:
self.form_fields = []
self.submitted = False
self.buttonText = "Submit"
yield
recipe_prompt = generate_prompt(self.ai_response)
self.imageLink = text2img(recipe_prompt)
def handle_reset(self):
FieldState.ingredient = ""
FieldState.quantity = ""
FieldState.unit = ""
FieldState.cookware = ""
yield
self.ai_response = ""
self.form_data = {}
self.form_fields = []
self.imageLink = ""
self.cantSubmit = True
self.onlyIngredients = False
yield

import reflex as rx
from lahacks.pages.components.recipe_image import output
from lahacks.states.form_state import DynamicFormState
from lahacks.states.field_state import FieldState
from lahacks.styles.styles import button_style
def dynamic_form():
return rx.vstack(
rx.form(
rx.vstack(
rx.foreach(
DynamicFormState.form_fields,
lambda field: rx.box(rx.vstack(
rx.vstack(rx.heading("Ingredient", size="4"),
rx.text(field[0], style={"overflow-wrap": "break-word", "word-wrap": "break-word"})),
rx.heading("Amount", size="4"),
rx.text(field[1] + " " + field[2]),
), border_width=1, border_color="gray-300", border_radius=10, padding=10, width="100%"),
),
rx.hstack(
rx.input(
placeholder="Ingredient name",
name="ingredient",
value=FieldState.ingredient,
on_change=FieldState.set_ingredient
),
rx.input(
placeholder="Amount",
name="amount",
on_change=FieldState.set_quantity,
value=FieldState.quantity
),
rx.select(["no unit", "grams", "oz", "fl oz", "gallon(s)", "piece(s)", "slice(s)", "can(s)", "jar(s)", "bottle(s)", "jug(s)", "bag(s)"], name="unit", placeholder="Units (optional)",
on_change=FieldState.set_unit, value=FieldState.unit),
rx.button("+", on_click=DynamicFormState.add_field(
FieldState.ingredient,
FieldState.quantity,
FieldState.unit,
), type="button", style=button_style),
rx.button("Clear", on_click=FieldState.reset_vals,
type="button", style=button_style),
),
rx.checkbox("Use only these ingredients?", size="3",
on_change=DynamicFormState.set_only_ingredients, checked=DynamicFormState.onlyIngredients, name="onlyIngredients"),
rx.input.root(rx.input(
placeholder="Enter your cookware (optional)",
name="cookware",
on_change=FieldState.set_cookware,
value=FieldState.cookware,
), width="100%"),
rx.text("Press the + button to add your ingredient."),
rx.spacer(),
rx.hstack(
rx.button(DynamicFormState.buttonText, type="submit",
disabled=DynamicFormState.cantSubmit, style=button_style),
rx.button(
"Reset", on_click=DynamicFormState.handle_reset, type="button", style=button_style),
rx.cond(
DynamicFormState.buttonText == "Generating...",
rx.html('''<dotlottie-player src="https://lottie.host/d395e1a4-28dc-4e60-bffd-8a8ed8844318/qvoNVQ79Bc.json" background="transparent" speed="1" style="width: 50px; height: 40px;" loop autoplay></dotlottie-player>'''),
),
align="center",
),
rx.cond(
DynamicFormState.ai_response != "",
rx.box(rx.text("View your ", size="4", as_="span"), rx.link(
"generated recipe!",
href="/output/",
underline="always",
size="4",
as_="span"
)),
),
height="100%",
),
on_mount=DynamicFormState.handle_reset,
on_submit=DynamicFormState.handle_submit,
reset_on_submit=True,
height="100%",
),
height="100%",
)
def index() -> rx.Component:
return rx.center(
rx.script(src="https://unpkg.com/@dotlottie/player-component@latest/dist/dotlottie-player.mjs",
custom_attrs={"type": "module"}),
rx.vstack(
rx.heading("Chef.ai", marginX="auto", paddingY=10),
rx.box(
dynamic_form(),
border_width=1,
border_color="gray-300",
border_radius=10,
height="80%",
padding=20,
),
height="100%",
),
height="100vh",
paddingX=10,
)
app = rx.App()
app.add_page(index)
app.add_page(output)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages