import { generateObject } from "ai"; import { z } from "zod"; import { resolveModel, type AiConfig } from "../factory"; import { dietaryTagsSchema, ingredientSchema, stepSchema } from "./recipe-schema"; const RegeneratedRecipeSchema = z.object({ title: z.string(), description: z.string().optional(), baseServings: z.number().int().min(1).max(100), prepMins: z.number().int().min(0).optional(), cookMins: z.number().int().min(0).optional(), difficulty: z.enum(["easy", "medium", "hard"]).optional(), dietaryTags: dietaryTagsSchema.optional(), ingredients: z.array(ingredientSchema(z.number())), steps: z.array(stepSchema), }); export type RegeneratedRecipe = z.infer; const LANG: Record = { en: "English", fr: "French" }; /** Revises a recipe draft already open in the editor per a free-text * instruction — "add more spice", "make it vegetarian", "double the yield". * Unlike adaptRecipe/suggestVariations, this never creates a new recipe or * variation record; it returns a full replacement draft for the caller to * merge into the in-progress form state, which the user still has to save. */ export async function regenerateRecipe( current: { title: string; description?: string | null; baseServings: number; difficulty?: string | null; ingredients: Array<{ rawName: string; quantity?: string | number | null; unit?: string | null }>; steps: Array<{ instruction: string }>; }, instruction: string, config?: AiConfig & { language?: string } ): Promise { const model = resolveModel(config); const lang = LANG[config?.language ?? "en"] ?? "English"; const langInstruction = lang !== "English" ? ` Write the entire recipe (title, description, ingredient names, step instructions) in ${lang}.` : ""; const currentText = [ `Title: ${current.title}`, current.description ? `Description: ${current.description}` : "", `Servings: ${current.baseServings}`, current.difficulty ? `Difficulty: ${current.difficulty}` : "", `Ingredients:\n${current.ingredients.map((i) => ` - ${i.quantity ? `${i.quantity} ` : ""}${i.unit ? `${i.unit} ` : ""}${i.rawName}`).join("\n")}`, `Steps:\n${current.steps.map((s, i) => ` ${i + 1}. ${s.instruction}`).join("\n")}`, ].filter(Boolean).join("\n"); const { object } = await generateObject({ model, schema: RegeneratedRecipeSchema, system: `You are revising an existing recipe draft per the user's instruction. Keep everything unchanged except what the instruction requires you to change — this is an edit, not a fresh rewrite. Return the complete updated recipe, not just the changed parts. For ingredient quantities: use numbers only, units separately.${langInstruction}`, prompt: `Current recipe draft:\n${currentText}\n\nInstruction: ${instruction}`, }); return object; }