Files
Epicure/apps/web/lib/ai/features/scale-recipe.ts
T
Arnaud d9d58fd01a feat(ai): Vercel AI SDK provider factory with BYOK and per-use-case model prefs
Provider factory (OpenAI/Anthropic/OpenRouter/Ollama). generateObject for all outputs.
Features: recipe generation, photo-to-recipe vision, variations, drink/meal pairings,
ingredient substitution, recipe adaptation, nutrition analysis, URL import, meal plan gen.
User BYOK keys (AES-256-GCM). Per-use-case model preferences (text/vision/mealPlan).
Site-level key override via admin settings.
2026-07-01 08:10:19 +02:00

53 lines
1.5 KiB
TypeScript

import { generateObject } from "ai";
import { z } from "zod";
import { resolveModel, type AiConfig } from "../factory";
const ScaledIngredientsSchema = z.object({
ingredients: z.array(
z.object({
rawName: z.string(),
quantity: z.string(),
unit: z.string().nullable(),
note: z.string().optional(),
})
),
});
export type ScaledIngredient = z.infer<typeof ScaledIngredientsSchema>["ingredients"][number];
type RecipeInput = {
title: string;
baseServings: number;
ingredients: Array<{
rawName: string;
quantity: string | null;
unit: string | null;
}>;
};
export async function scaleRecipe(
recipe: RecipeInput,
targetServings: number,
originalServings: number,
config?: AiConfig,
locale?: string
): Promise<ScaledIngredient[]> {
const model = resolveModel(config);
const ingredientList = recipe.ingredients
.map((ing) => {
const parts = [ing.quantity, ing.unit, ing.rawName].filter(Boolean);
return `- ${parts.join(" ")}`;
})
.join("\n");
const { object } = await generateObject({
model,
schema: ScaledIngredientsSchema,
system: `You are a culinary scaling expert. Scale recipe ingredients from ${originalServings} to ${targetServings} servings. Use practical quantities — no 0.33 eggs (round to whole), no 0.17 cans (use 0.25 or 0.5), avoid impractical fractions. Substitute whole units where sensible. Add a note when rounding significantly.`,
prompt: `Recipe: "${recipe.title}"\n\nIngredients to scale:\n${ingredientList}`,
});
return object.ingredients;
}