Files
Epicure/apps/web/lib/ai/features/suggest-variations.ts
T
2026-07-01 11:10:37 +02:00

70 lines
2.4 KiB
TypeScript

import { generateObject } from "ai";
import { z } from "zod";
import { resolveModel, type AiConfig } from "../factory";
const VariationsOutputSchema = z.object({
variations: z.array(z.object({
title: z.string(),
description: z.string(),
changedIngredients: z.array(z.object({
original: z.string(),
replacement: z.string(),
note: z.string().optional(),
})),
changedSteps: z.array(z.object({
stepNumber: z.number().int(),
change: z.string(),
})).optional(),
dietaryTags: z.object({
vegan: z.boolean().optional(),
vegetarian: z.boolean().optional(),
glutenFree: z.boolean().optional(),
dairyFree: z.boolean().optional(),
nutFree: z.boolean().optional(),
halal: z.boolean().optional(),
kosher: z.boolean().optional(),
}).optional(),
})),
});
export type VariationSuggestion = z.infer<typeof VariationsOutputSchema>["variations"][number];
const LANG: Record<string, string> = { en: "English", fr: "French" };
export async function suggestVariations(
recipe: {
title: string;
description?: string | null;
ingredients: Array<{ rawName: string; quantity?: string | null; unit?: string | null }>;
steps: Array<{ instruction: string }>;
},
count = 3,
config?: AiConfig & { userContext?: string },
directions?: string,
locale?: string
): Promise<VariationSuggestion[]> {
const model = resolveModel(config);
const lang = LANG[locale ?? "en"] ?? "English";
const recipeText = [
`Recipe: ${recipe.title}`,
recipe.description ? `Description: ${recipe.description}` : "",
`Ingredients: ${recipe.ingredients.map((i) => `${i.quantity ?? ""} ${i.unit ?? ""} ${i.rawName}`.trim()).join(", ")}`,
`Steps: ${recipe.steps.map((s, i) => `${i + 1}. ${s.instruction}`).join(" | ")}`,
].filter(Boolean).join("\n");
const directionsClause = directions?.trim()
? `\n\nUser directions: ${directions.trim()}`
: "";
const { object } = await generateObject({
model,
schema: VariationsOutputSchema,
system:
`You are a creative chef. Suggest meaningful variations of recipes — dietary adaptations, flavor profiles, technique changes. Each variation should be distinct and practical. Respond in ${lang}.${config?.userContext ? `\n\nUser preferences and context:\n${config.userContext}` : ""}`,
prompt: `Suggest ${count} variations of this recipe:\n\n${recipeText}${directionsClause}`,
});
return object.variations;
}