recipe-gen / app.py
OmPrakashSingh1704's picture
Update app.py
dfcffbf verified
raw
history blame
10.3 kB
import streamlit as st
import re,torch
import json,os
from transformers import AutoModelForCausalLM, AutoTokenizer
from datetime import datetime
from huggingface_hub import login
login(token=os.getenv("TOKEN"))
# Load model and tokenizer
model = AutoModelForCausalLM.from_pretrained(
"google/gemma-2b",
torch_dtype="auto",
device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained("google/gemma-2b")
if 'recipe' not in st.session_state:
st.session_state.recipe = None
if 'recipe_saved' not in st.session_state:
st.session_state.recipe_saved = None
if 'user_direction' not in st.session_state:
st.session_state.user_direction = None
if 'serving_size' not in st.session_state:
st.session_state.serving_size = 2
if 'selected_difficulty' not in st.session_state:
st.session_state.selected_difficulty = "Quick & Easy"
if 'exclusions' not in st.session_state:
st.session_state.exclusions = None
def create_detailed_prompt(user_direction, exclusions, serving_size, difficulty):
if difficulty == "Quick & Easy":
prompt = (
f"Provide a 'Quick and Easy' recipe for {user_direction} that excludes {exclusions} and has a serving size of {serving_size}. "
f"It should require as few ingredients as possible and should be ready in as little time as possible. "
f"The steps should be simple, and the ingredients should be commonly found in a household pantry. "
f"Provide a detailed ingredient list and step-by-step guide that explains the instructions to prepare in detail."
)
elif difficulty == "Intermediate":
prompt = (
f"Provide a classic recipe for {user_direction} that excludes {exclusions} and has a serving size of {serving_size}. "
f"The recipe should offer a bit of a cooking challenge but should not require professional skills. "
f"The recipe should feature traditional ingredients and techniques that are authentic to its cuisine. "
f"Provide a detailed ingredient list and step-by-step guide that explains the instructions to prepare in detail."
)
elif difficulty == "Professional":
prompt = (
f"Provide a advanced recipe for {user_direction} that excludes {exclusions} and has a serving size of {serving_size}. "
f"The recipe should push the boundaries of culinary arts, integrating unique ingredients, advanced cooking techniques, and innovative presentations. "
f"The recipe should be able to be served at a high-end restaurant or would impress at a gourmet food competition. "
f"Provide a detailed ingredient list and step-by-step guide that explains the instructions to prepare in detail."
)
return prompt
def generate_recipe(user_inputs):
with st.spinner('Building the perfect recipe...'):
provide_recipe_schema = {
'type': 'function',
'function': {
'name': 'provide_recipe',
'description': 'Provides a detailed recipe strictly adhering to the user input/specifications, especially ingredient exclusions and the recipe difficulty',
'parameters': {
'type': 'object',
'properties': {
'name': {
'type': 'string',
'description': 'A creative name for the recipe'
},
'description': {
'type': 'string',
'description': 'a brief one-sentence description of the provided recipe'
},
'ingredients': {
'type': 'array',
'items': {
'type': 'object',
'properties': {
'name': {
'type': 'string',
'description': 'Quantity and name of the ingredient'
}
}
}
},
'instructions': {
'type': 'array',
'items': {
'type': 'object',
'properties': {
'step_number': {
'type': 'number',
'description': 'The sequence number of this step'
},
'instruction': {
'type': 'string',
'description': 'Detailed description of what to do in this step'
}
}
}
}
},
'required': [
'name',
'description',
'ingredients',
'instructions'
]
}
}
}
prompt = create_detailed_prompt(user_inputs['user_direction'], user_inputs['exclusions'], user_inputs['serving_size'], user_inputs['difficulty'])
messages = [{"role": "user", "content": prompt}]
tool_section = "\n".join([f"{tool['function']['name']}({json.dumps(tool['function']['parameters'])})" for tool in [provide_recipe_schema]])
text = f"{prompt}\n\nTools:\n{tool_section}"
# Tokenize and move to the correct device
model_inputs = tokenizer([text], return_tensors="pt")
# text = tokenizer.apply_chat_template(
# messages,
# tokenize=False,
# add_generation_prompt=True,
# tools=[provide_recipe_schema]
# )
# Tokenize and move to the correct device
# model_inputs = tokenizer([text], return_tensors="pt")
torch.cuda.empty_cache()
with torch.no_grad():
generated_ids = model.generate(
model_inputs.input_ids,
max_new_tokens=512,
)
generated_ids = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]
st.session_state.recipe = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
st.session_state.recipe_saved = False
def clear_inputs():
st.session_state.user_direction = None
st.session_state.exclusions = None
st.session_state.serving_size = 2
st.session_state.selected_difficulty = "Quick & Easy"
st.title("Let's get cooking")
st.session_state.user_direction = st.text_area(
"What do you want to cook? Describe anything - a dish, cuisine, event, or vibe.",
value = st.session_state.user_direction,
placeholder="quick snack, asian style bowl with either noodles or rice, something italian",
)
st.session_state.serving_size = st.number_input(
"How many servings would you like to cook?",
min_value=1,
max_value=100,
value=st.session_state.serving_size,
step=1
)
difficulty_dictionary = {
"Quick & Easy": {
"description": "Easy recipes with straightforward instructions. Ideal for beginners or those seeking quick and simple cooking.",
},
"Intermediate": {
"description": "Recipes with some intricate steps that invite a little challenge. Perfect for regular cooks wanting to expand their repertoire with new ingredients and techniques.",
},
"Professional": {
"description": "Complex recipes that demand a high level of skill and precision. Suited for seasoned cooks aspiring to professional-level sophistication and creativity.",
}
}
st.session_state.selected_difficulty = st.radio(
"Choose a difficulty level for your recipe.",
[
list(difficulty_dictionary.keys())[0],
list(difficulty_dictionary.keys())[1],
list(difficulty_dictionary.keys())[2]
],
captions = [
difficulty_dictionary["Quick & Easy"]["description"],
difficulty_dictionary["Intermediate"]["description"],
difficulty_dictionary["Professional"]["description"]
],
index=list(difficulty_dictionary).index(st.session_state.selected_difficulty)
)
st.session_state.exclusions = st.text_area(
"Any ingredients you want to exclude?",
value = st.session_state.exclusions,
placeholder="gluten, dairy, nuts, cilantro",
)
fancy_exclusions =""
if st.session_state.selected_difficulty == "Professional":
exclude_fancy = st.checkbox(
"Exclude cliche professional ingredients? (gold leaf, truffle, edible flowers, microgreens)",
value=True)
fancy_exclusions = "gold leaf, truffle, edible flowers, microgreens, gold dust"
user_inputs = {
"user_direction" : st.session_state.user_direction,
"exclusions": f"{st.session_state.exclusions}, {fancy_exclusions}",
"serving_size": st.session_state.serving_size,
"difficulty": st.session_state.selected_difficulty
}
button_cols_submit = st.columns([1, 1, 4])
with button_cols_submit[0]:
st.button(label='Submit', on_click=generate_recipe, kwargs=dict(user_inputs=user_inputs), type="primary", use_container_width=True)
with button_cols_submit[1]:
st.button(label='Reset', on_click=clear_inputs, type="secondary", use_container_width=True)
with button_cols_submit[2]:
st.empty()
if st.session_state.recipe is not None:
st.divider()
print(st.session_state.recipe)
recipe = json.loads(st.session_state.recipe)
recipe_md = ''
recipe_md += f'# {recipe["name"]} \n\n'
recipe_md += f'{recipe["description"]} \n\n'
recipe_md += '## Ingredients: \n'
for ingredient in recipe['ingredients']:
recipe_md += f"- {ingredient['name']} \n"
recipe_md += '\n## Instructions:\n'
for instruction in recipe['instructions']:
recipe_md += f"{instruction['step_number']}. {instruction['instruction']} \n"
recipe['md'] = recipe_md
recipe['timestamp'] = str(datetime.now())
st.markdown(recipe_md)
st.write("")