Skip to content

Instantly share code, notes, and snippets.

@adamgins
Created May 20, 2023 22:42
Show Gist options
  • Save adamgins/ad7f18967df05c43bcdf4406e3ef94e9 to your computer and use it in GitHub Desktop.
Save adamgins/ad7f18967df05c43bcdf4406e3ef94e9 to your computer and use it in GitHub Desktop.
Buzzy Example - HTML field call OpenAI to generate recipes
<!DOCTYPE html>
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/* Minimal reset */
*, *::before, *::after {
box-sizing: border-box;
}
* {
margin: 0;
padding: 0;
font: inherit;
}
html {
color-scheme: light dark;
}
body {
min-height: 100vh;
}
img, picture, svg, video {
display: block;
max-width: 100%;
}
/* Custom */
html {
font-family: 'Inter', Arial, Helvetica, sans-serif;
font-size: 16px;
line-height: 24px;
color-scheme: light;
color: #697586;
/*background: #F8FAFC;*/
}
.customfilter {
display: flex;
flex-direction: row;
align-items: flex-end;
gap: 8px;
}
/*.customfilter-section {*/
/* display: flex;*/
/* align-items: center;*/
/* flex: 1 0 0;*/
/* gap: 8px;*/
/*}*/
/*.customfilter label {*/
/* display: inline;*/
/* font-weight: 500;*/
/*}*/
/*.customfilter input {*/
/* display: inline;*/
/* width: 100%;*/
/* color: #FFFFFF;*/
/* background: rgba(255, 255, 255, 0.1);*/
/* border: 2px solid rgba(255, 255, 255, 0.1);*/
/* border-radius: 8px;*/
/* padding: 8px 10px;*/
/* font-weight: 500;*/
/* font-size: 16px;*/
/* line-height: 24px;*/
/*}*/
/*.customfilter-buttons-row {*/
/* display: flex;*/
/* gap: 10px;*/
/* padding: 0 20px 20px 20px;*/
/* overflow: auto;*/
/*}*/
.customfilter button {
display: block;
color: #697586;
background: #FFFFFF;
border: 1px solid #E3E8EF;
padding: 8px 24px;
font-weight: 500;
font-size: 16px;
line-height: 24px;
white-space: nowrap;
cursor: pointer;
border-radius: 9999px;
}
.customfilter button:disabled {
color: #9AA4B2;
background: #EEF2F6;
border: 2px solid #EEF2F6;
cursor: auto;
}
.customfilter button.button-primary {
color: #D444F1;
background: #FFFFFF;
border: 1px solid #E3E8EF;
}
.customfilter button.button-primary:disabled {
/* same as default button */
}
/* Helper */
.sr-only {
position:absolute;
left:-10000px;
top:auto;
width:1px;
height:1px;
overflow:hidden;
}
</style>
<script>
const SLOT_DURATION = 0.5;
let appData;
let initialized = false;
let params;
let selectedDate = new Date( new Date().toDateString() + " 12:00:00 AM").getTime();
let selectedSlot;
console.log('IFRAME WINDOW', window, 'PARENT', parent, 'TOP PARENT', window.top.parent)
function parseJSON(str) {
try {
const parsedJSON =JSON.parse(str);
return parsedJSON;
} catch (error) {
return false;
}
}
function generateRecipes(){
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", "Bearer <use your OpenAI AI key here>");
const raw = JSON.stringify({
"messages": [
{
"role": "user",
"content": `Generate the names of 5 recipes, that include some of these ingredients: ${params.rowJSON.suppliedIngredients} compatible with these dietary restrictions: ${params.rowJSON.dietaryRestrictions} and avoiding these ingredients or substances: ${params.rowJSON.allergies} . Return an array of JSON objects ONLYin this format: [{name:text}] as valid javascript array only.`
}
],
"temperature": 0,
"max_tokens": 3000,
"top_p": 1,
"model": "gpt-3.5-turbo",
"stream": false
});
const requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
console.log('about to geenrate recipes with raw', raw);
fetch("https://api.openai.com/v1/chat/completions", requestOptions)
.then(response => response.text())
.then(result => {
const parsedResult = JSON.parse(result);
console.log('RESULT', parsedResult);
const { choices } = parsedResult || {};
if (choices && Array.isArray(choices) && choices.length > 0){
console.log('firstChoice', choices[0]);
const { message }= choices[0] || {};
const { content } = message || {};
const parsedRecipes = content && parseJSON(content);
if (parsedRecipes) {
console.log('parsedRecipes', parsedRecipes);
// const {recipes} = parsedRecipes || {};
console.log('about to process recipes',parsedRecipes);
if (Array.isArray(parsedRecipes)){
parsedRecipes.forEach(recipe=>{
console.log('about to add recipe', recipe);
const { name } = recipe || {};
if (name) {
const rawRecipe = JSON.stringify({
"messages": [
{
"role": "user",
"content": `Generate a single '${name}' recipe, that include some of these supplied ingredients: ${params.rowJSON.suppliedIngredients}. missingIngredients are ingredients for the recipe, less the supplied ingredients.`
},
{
"role": "user",
"content": `Your response should be in JSON. Return the result as JSON object ONLY, no text outside of JSON, that contains: title as text; ingredients as markdown; instructions: markdown; 'missingIngredients' as text; prepTime as number of minutes to prepare; kilojoules: number; calories: number; suitableFor: text as suitable for dietary restrictions; savedCost: number which is combined cost of ingredients: '${params.rowJSON.suppliedIngredients}'. Ensure response is a valid JSON object only, nothing else.`
}
],
"temperature": 0,
"max_tokens": 3000,
"top_p": 1,
"model": "gpt-4",
"stream": false
});
// Log added with Raw Recipe
console.log('JSON with raw recipe total', rawRecipe);
// end Log added with Raw Recipe
const recipeRequestOptions = {
method: 'POST',
headers: myHeaders,
body: rawRecipe,
redirect: 'follow'
};
fetch("https://api.openai.com/v1/chat/completions", recipeRequestOptions)
.then(response => response.text())
.then(result => {
// console.log('RECIPE result', result);
const parsedRecipeResult = JSON.parse(result);
// console.log('parsedResultRecipe:', parsedRecipeResult);
const { choices } = parsedRecipeResult || {};
if (choices && Array.isArray(choices) && choices.length > 0){
console.log('firstChoice', choices[0]);
const { message }= choices[0] || {};
const { content } = message || {};
const recipe = content && parseJSON(content);
if (recipe) {
console.log('single recipe:', recipe);
window.top.parent.postMessage({
frameToken:params.frameToken,
action:'insertmicroapprow',
body: {
"embeddingRowID":params.rowJSON._id,
"microAppID":"<use the datatable / microapp ID you want to insert data into here>",
"rowData":{
title: recipe.title,
ingredients: recipe.ingredients,
method: recipe.instructions,
calories: recipe.calories,
kilijoules: recipe.kilijoules,
missingIngredients: recipe.missingIngredients,
prepTime: recipe.time,
servings: recipe.servings,
type: recipe.type,
savedCost: recipe.savedCost
},
"ignoreActionRules":true
}
},'*');
}
}
})
}
})
}
} else {
alert(content);
}
}
})
.catch(error =>{
console.log('error', error);
alter('')
});
// const newAge = 100 * 30;
// alert('Generate!');
// window.top.parent.postMessage({
// frameToken:params.frameToken,
// action:'updatemicroapprow',
// body: {
// rowID: "b83c900e25c4c12462eee259",
// rowData:{
// "Age":newAge
// }
// }
// },'*');
}
function hoursMillis(hours){
return Math.ceil(hours * 60 * 60 * 1000);
};
function filterResults(){
console.log('filterResults', params);
if (!params) return;
const lowVal = Number(document.getElementById('lowValue')?.value);
const highVal = Number(document.getElementById('highValue')?.value);
console.log(`lowVal: ${lowVal} highVal: ${highVal}`);
window.top.parent.postMessage({
frameToken:params.frameToken,
action:'filterview',
body: {
microAppID:"b83c900e25c4c12462eee259",
subLimit: 100,
embeddingRowID:params.rowJSON._id,
viewFilters:[ {sortVal5: {$gte:lowVal}},{sortVal5: {$lte:highVal}}],
viewFilterIsMongoQuery: true,
filterContext: 'My age filter'
}
},'*');
}
window.addEventListener('message', function (e) {
var mainWindow = e.source;
// console.log('EVENT LANDED FROM', e);
const { data } = e || {};
appData = data;
const { rowJSON, resourceJSON, frameToken } = appData || {};
params = { rowJSON, resourceJSON, frameToken };
});
</script>
</head>
<body>
<div class="customfilter">
<button type="button" class="button-primary" onclick="generateRecipes();">Generate Recipes</button>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment