70 lines
2.4 KiB
TypeScript
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;
|
|
}
|