fix(detection+tandoor): handle stepless Instagram recipes
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 33s

Many Instagram recipe posts list ingredients without preparation steps,
directing users to the 'link in bio' for the full recipe.

- Detection prompt: removed step requirement entirely — title + 2
  ingredients is sufficient to detect a recipe
- tandoor.ts: when steps array is null/empty, create a single
  placeholder step so all ingredients are preserved in Tandoor

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Giancarmine Salucci
2026-05-13 02:45:21 +02:00
parent d09bf80088
commit a389b0db15
2 changed files with 18 additions and 14 deletions

View File

@@ -9,19 +9,17 @@
export const RECIPE_DETECTION_PROMPT = `You are a recipe detector for social media posts.
Your task: Determine if the text contains a recipe or cooking instructions.
Your task: Determine if the text contains recipe content (ingredients list, cooking info, or dish preparation).
REQUIREMENTS FOR "YES" — text must have ALL of:
SAY "yes" if the text has:
1. A dish name or title
2. At least 3 ingredients (quantities are NOT required — social media posts often omit them)
3. At least 1 cooking or preparation step
2. At least 2 ingredients (quantities optional — many posts omit them)
IGNORE:
- Hashtags (#recipe, #food, etc.)
- Mentions (@username)
- Emojis
- Like counts, comments, social metadata
- Promotional text ("follow me", "save this", "tag a friend")
Steps are NOT required — many Instagram posts list only ingredients and link to the full recipe elsewhere.
SAY "no" only if the text has NO ingredients at all (e.g. pure food appreciation posts, restaurant reviews, memes).
IGNORE: hashtags, @mentions, emojis, "save this", "follow me", "link in bio" phrases.
OUTPUT: Answer with ONLY 'yes' or 'no' - nothing else.
@@ -30,11 +28,14 @@ EXAMPLES:
Text: "🍝 Pasta al Pomodoro Ingredients: pasta, tomatoes, garlic. Boil pasta. Sauté garlic. Add tomatoes. Mix! #italianfood @chef"
Answer: yes
Text: "Panini al Latte Ingredienti: 250g farina, 35g zucchero, 8g lievito, 130g latte, 1 uovo, 40g burro, gocce di cioccolato. Trovi la ricetta completa al link in bio."
Answer: yes
Text: "Amazing dinner tonight! 😍 So delicious! 🔥 #foodporn"
Answer: no
Text: "Brioche velocissime Per 7 brioche: rotolo di pasta sfoglia, prosciutto cotto, provola. Arrotola la sfoglia con i ripieni, taglia in pezzi, inforna a 180°C."
Answer: yes
Text: "Best restaurant in Milano! You have to try it 🙌"
Answer: no
`;
export const RECIPE_EXTRACTION_PROMPT = `You are an EXPERT RECIPE EXTRACTOR specialized in parsing recipes from social media posts.

View File

@@ -234,10 +234,13 @@ function parseAmount(amountStr: string): number | null {
* Includes ingredients partitioned across steps
*/
function buildTandoorRecipeDTO(recipe: ExtractedRecipe): TandoorRecipeDTO {
const stepCount = recipe.steps?.length || 1;
// When the caption has no steps (e.g. "full recipe at link in bio"), create
// a single placeholder step so ingredients are preserved in Tandoor.
const recipeSteps = recipe.steps?.length ? recipe.steps : ['Vedi la ricetta completa al link in bio.'];
const stepCount = recipeSteps.length;
const ingredientPartitions = partitionIngredientsAcrossSteps(recipe.ingredients || [], stepCount);
const steps: TandoorRecipeDTO['steps'] = (recipe.steps || []).map((instruction, index) => {
const steps: TandoorRecipeDTO['steps'] = recipeSteps.map((instruction, index) => {
// Map ingredients, converting unparseable amounts to 1 q.b.
const mappedIngredients = (ingredientPartitions[index] || []).map((ing) => {
const amount = parseAmount(ing.amount);