Skip to content

Instantly share code, notes, and snippets.

@lindell
Created September 10, 2023 10:39
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lindell/2a08c49fccb070887a037357b5194e3d to your computer and use it in GitHub Desktop.
Save lindell/2a08c49fccb070887a037357b5194e3d to your computer and use it in GitHub Desktop.
import OpenAI from 'openai';
import clipboard from 'clipboardy';
const openai = new OpenAI({
apiKey: null, // defaults to process.env["OPENAI_API_KEY"]
});
const instruction = `You are an ingredient extractor.
Given a set of ingredients and steps in a recipe. Determine which ingredients are mentioned in each steps.`;
const schema = {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"step": {
"type": "number",
"description": "The step number"
},
"ingredients": {
"type": "array",
"description": "A list of ingredients used in this step",
"items": {
"type": "number",
"description": "The ingredient number"
}
},
},
},
},
},
}
async function main() {
const data = clipboard.readSync();
const recipe = JSON.parse(data);
const ingredients = recipe.recipeIngredient;
const ingredientList = ingredients
.map((ingredient, i) => {
if (ingredient.title) {
return `\n${ingredient.title})\n${i + 1}: ${ingredientName(ingredient)}`;
} else {
return `${i + 1}: ${ingredientName(ingredient)}`;
}
})
.join("\n");
const unusedIngredients = new Set(ingredients.map((_, i) => i + 1));
const steps = recipe.
recipeInstructions.map((instruction, i) => `${i + 1}) ${instruction.text}`).
join('\n');
const toSend = `The list of ingredients:\n${ingredientList}\n\nThe recipe steps:\n${steps}`;
console.log(`This will be sent to the API:\n${toSend}`);
const split = await getIngredientSplit(toSend);
console.log(JSON.stringify(split, null, 2));
recipe.recipeInstructions.forEach((instruction, i) => {
const stepSplits = split.steps.find(s => s.step === i + 1);
if (!stepSplits) {
return;
}
const refList = stepSplits.ingredients.map(ingredientNo => {
let refId;
if (unusedIngredients.has(ingredientNo)) {
refId = ingredients[ingredientNo - 1].referenceId;
unusedIngredients.delete(ingredientNo);
}
return {
referenceId: refId,
};
}).filter(ref => ref.referenceId);
instruction.ingredientReferences = refList;
});
const final = JSON.stringify(recipe, null, 2);
clipboard.writeSync(final);
// Print how it was split for easier debugging
const stepsWithIngredients = recipe.
recipeInstructions.map((instruction, i) => {
const instructions = `${i + 1}) ${instruction.text}`;
const stepSplits = split.steps.find(s => s.step === i + 1);
const ingredientList = stepSplits.ingredients.
map(ingredientNo => ingredientName(ingredients[ingredientNo - 1])).
join(', ');
return `${instructions}\n${ingredientList}`
}).
join('\n\n');
console.log(stepsWithIngredients);
if (unusedIngredients.size !== 0) {
console.warn("Ingredient list is not empty, still contains: "
+ Array.from(unusedIngredients.values()).map(n => ingredientName(ingredients[n-1])).join(", ")
);
}
}
function ingredientName(ingredient) {
return ingredient.food?.name || ingredient.display;
}
async function getIngredientSplit(toSend) {
const completion = await openai.chat.completions.create({
model: 'gpt-4',
// model: 'gpt-3.5-turbo',
// model: 'gpt-3.5-turbo-16k',
messages: [
{ role: "system", "content": instruction },
{ role: 'user', content: toSend },
],
functions: [{ name: "split_into_steps", parameters: schema }],
function_call: { name: "split_into_steps" },
temperature: 0.2,
});
console.log(completion);
const ret = completion.choices[0].message.function_call.arguments;
return JSON.parse(ret);
}
main().catch(e => console.log(e));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment