import streamlit as st import numpy as np import pandas as pd import re import json from openai import OpenAI import secrets import pyperclip client = OpenAI( api_key = st.secrets["open_ai_key"] ) # state management if 'gpt_response' not in st.session_state: st.session_state.gpt_response = None if "copied" not in st.session_state: st.session_state.copied = [] # functions 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...'): functions = [ { "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}] st.session_state.gpt_response = client.chat.completions.create( model="gpt-4-1106-preview", messages=messages, temperature=0.75, top_p=0.75, functions=functions, function_call={"name":"provide_recipe"}, # auto is default, but we'll be explicit ) def on_copy_click(text): st.session_state.copied.append(text) pyperclip.copy(text) st.toast(f"Copied to clipboard!", icon='✅' ) def create_safe_filename(recipe_name): # Convert to lowercase safe_name = recipe_name.lower() # Replace spaces with underscores safe_name = safe_name.replace(" ", "_") # Remove or replace other non-alphanumeric characters (excluding underscore) safe_name = re.sub(r"[^a-zA-Z0-9_]", "", safe_name) # Truncate the name if it's very long safe_name = (safe_name[:50]) if len(safe_name) > 50 else safe_name # Generate a random URL-safe text string unique_token = secrets.token_hex(8) # This creates a 16-character hexadecimal string # Append the token to the safe recipe name safe_filename = f"{unique_token}_{safe_name}" # Assuming you want a .txt file return safe_filename # app st.title("Let's get cooking") user_direction = st.text_area( "What do you want to cook? Describe anything - a dish, cuisine, event, or vibe.", placeholder="quick snack, asian style bowl with either noodles or rice, something italian", ) serving_size = st.number_input( "How many servings would you like to cook?", min_value=1, max_value=100, value=2, 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.", } } 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"] ] ) exclusions = st.text_area( "Any ingredients you want to exclude?", placeholder="gluten, dairy, nuts, cilantro", ) fancy_exclusions ="" if 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" : user_direction, "exclusions": f"{exclusions}, {fancy_exclusions}", "serving_size": serving_size, "difficulty": selected_difficulty } st.button(label='Submit', on_click=generate_recipe, kwargs=dict(user_inputs=user_inputs)) if st.session_state.gpt_response is not None: st.divider() recipe = json.loads(st.session_state.gpt_response.choices[0].message.function_call.arguments) recipe_md = '' recipe_md += f'# {recipe["name"]} \n\n' recipe_md += f'{recipe["description"]} \n\n' recipe_md += f'## Ingredients: \n' for ingredient in recipe['ingredients']: recipe_md += f"- {ingredient['name']} \n" recipe_md += f'\n## Instructions:\n' for instruction in recipe['instructions']: recipe_md += f"{instruction['step_number']}. {instruction['instruction']} \n" recipe['md'] = recipe_md st.markdown(recipe_md) #st.button("Copy to Clipboard", on_click=on_copy_click, args=(recipe_md,)) # write to file # filename = create_safe_filename(recipe["name"]) # with open(f"/data/generated/{filename}.json", "w") as f: # json.dump(recipe, f)