Culinary Chronicles 🍝🤖
Inspired by the "Retour de Voyage, Les Recettes" book from Social Food, I'm eager to document the dishes that leave a lasting impression during my travels. I want to try recreating them at home later. To assist me in this endeavor, I created a simple tool: a Google Form connected to OpenAI. It sends a log of recipes straight to my mailbox for future reference!
TRY THE DEMO HERE (beta 0.1)
I invite you to fill up this ↪ google form and check your mailbox instantly after. A recipe card should land shortly!
And please give me feedback, i’d love to brainstorm on how to make this better.
Example
Dish: Chicken Tagine / Restaurant: Cafe Mogador / City: NYC
End-to-End Prototype Tutorial
The "Culinary Chronicles" prototype leverages a Google Form as an input UI, bypassing the need for a custom web or mobile application. By utilizing Google Cloud Functions for API hosting, I avoided building a server from scratch. Prototyping allows for iterations and adjustments based on actual needs before investing extensive time in development.
Here’s the workflow I followed to build this demo:
Google Form:
Created a free Google Form to serve as the front end, collecting the names of dishes, restaurants, and cities. Learn more.
Google Sheets:
Linked the Google Form responses to a Google Sheet. Learn more.
Google Apps Script:
Set up an extension (Apps Script) on the Google Sheet.
Wrote a script in Google Apps Script (which is based on JavaScript) that triggers each time a form is submitted. This script sends a POST HTTP request with a payload containing the submitted email, dish name, restaurant, and city. Learn more.
I attached some sample code below 👀.
Google Cloud Functions:
Set up a project on Google Cloud and created a new function. Google provides new customers with $300, which gave me time to understand the technology and evaluate its fit for my project. Learn more.
Configured a Python function associated with a URL using the console UI. Any programming language can be used, but I chose Python 3.12. Don’t forget to list your dependencies in the requirements.txt file. Learn more.
Used the console to define environmental variables, saving my Gmail SMTP and OpenAI API keys. Learn more.
Python Script for Core Engine:
Interfaced with the OpenAI Python library to set up the Assistant, feed prompts, and fetch results. Documentation for setting up an OpenAI developer account and API usage is available. Learn more.
Leveraged the newly released GPT-4 models, which are cheaper than GPT-4.
I attached some sample code below 👀.
Email Results:
Used SMTP in the Python script to send the results from OpenAI to the participant’s email address. If using a personal Gmail account, you might need to set up an App password due to Google's security protocols. Learn more about Gmail SMTP server and App passwords.
Iteration and Debugging:
While this overview presents a linear process, the actual development involved canonical progress. I tackled steps 5 and 6 first, followed by steps 4, 1, 2, and 3, with extensive debugging at each phase. GPT assisted with writing some scripts, which I then tweaked. Note that GPT can be error-prone and may not always have up-to-date library nomenclature, so coding knowledge is crucial.
And voilà! The pipeline is ready. While this summary makes it sound easy, each step involved detailed work and troubleshooting.
Sample Code
Step 3
function onFormSubmit(e) { var responses = e.values; var email = responses[1]; var dish = responses[2]; var restaurant = responses[3]; var city = responses[4]; var description = responses[5]; var payload = { email: email, dish: dish, restaurant: restaurant, description: description }; var options = { method: 'post', contentType: 'application/json', payload: JSON.stringify(payload) }; // Replace with your Cloud Function URL var url = 'https://testapi.cloudfunctions.net/function-restaurant-favorite-recipe'; UrlFetchApp.fetch(url, options); } function setUpTrigger() { var sheet = SpreadsheetApp.getActiveSpreadsheet(); ScriptApp.newTrigger('onFormSubmit') .forSpreadsheet(sheet) .onFormSubmit() .create(); }
Step 5
def ask_recipe_card(dish, restaurant, city, description): openai_key = os.getenv('OPENAI_API_KEY') client = OpenAI(api_key = openai_key) try: prompt = "Prompt: < Dish: " + dish if restaurant: prompt += " / Restaurant: " + restaurant if city: prompt += " / City: " + city if description: prompt += " / Details: " + description chat_completion = client.chat.completions.create( messages=[ { "role": "system", "content": ( "You are a helpful assistant that provides detailed and accurate recipes. When given a dish name, restaurant name, and city, generate a detailed recipe card in HTML and CSS. The recipe card should include:\n" "1. An introduction that briefly describes the dish and its origin, ensuring the city provided in the input is correctly referenced in the output and not inferred from the dish name or other context." "2. A list of ingredients needed for the dish, including the number of servings.\n" "3. Step-by-step preparation instructions.\n" "4. Tips for presentation and serving, including drink pairings (both alcoholic and non-alcoholic).\n" "5. Any additional notes or variations if relevant.\n" "6. Fun facts and emojis to make the description engaging and fun.\n" "7. A disclaimer stating that the recipe is an educated guess and not the official restaurant recipe.\n" "8. A message encouraging recipients to reply with any feedback to improve the experience.\n" "Use a casual and fun tone throughout the recipe card, incorporating playful language, emojis, and a friendly, encouraging attitude.\n" "Format the recipe in HTML with the following CSS styling to ensure it is visually appealing and easy to read in an email client like Gmail. Use appropriate HTML tags and CSS styles for headings, lists, and sections.\n" "Provide several options for variations where possible.\n\n" "**Do not use markdown formatting or include '```html' in your output.**\n\n" "Here's the CSS to use:\n" "<style>\n" " body {\n" " font-family: Arial, sans-serif;\n" " line-height: 1.6;\n" " color: #333;\n" " background-color: #f9f9f9;\n" " padding: 20px;\n" " }\n" " .container {\n" " max-width: 600px;\n" " margin: 0 auto;\n" " background-color: #fff;\n" " border: 1px solid #ddd;\n" " border-radius: 8px;\n" " padding: 20px;\n" " }\n" " h1 {\n" " color: #d23600;\n" " font-size: 24px;\n" " }\n" " h2 {\n" " border-bottom: 2px solid #d23600;\n" " padding-bottom: 5px;\n" " font-size: 20px;\n" " }\n" " .section {\n" " margin-bottom: 20px;\n" " }\n" " .ingredient, .step {\n" " margin-bottom: 10px;\n" " }\n" " .ingredient-title, .step-title {\n" " font-weight: bold;\n" " color: #d23600;\n" " }\n" " footer {\n" " text-align: center;\n" " font-size: 14px;\n" " color: #777;\n" " }\n" "</style>\n" ) }, { "role": "user", "content": prompt } ], model="gpt-4o", ) return chat_completion.choices[0].message.content except Exception as e: return str(e)
Beta 0.1 improvement ideas from initial testers
Add images in the recipe report: end dish, and step by step images
Allow an image as input to improve the recipe suggestion, to better tailor to restaurant presentation
City and Restaurant as optional, to make the product more generic. What if you liked a dish served in another platform?
Output: PDF recipe, ready to be saved