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.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import { generateObject } from "ai";
|
||||
import { z } from "zod";
|
||||
import { resolveModel, type AiConfig } from "../factory";
|
||||
|
||||
const ImportedRecipeSchema = z.object({
|
||||
title: z.string(),
|
||||
description: z.string().optional(),
|
||||
baseServings: z.number().int().min(1).optional(),
|
||||
prepMins: z.number().int().min(0).optional(),
|
||||
cookMins: z.number().int().min(0).optional(),
|
||||
difficulty: z.enum(["easy", "medium", "hard"]).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(),
|
||||
ingredients: z.array(z.object({
|
||||
rawName: z.string(),
|
||||
quantity: z.string().optional(),
|
||||
unit: z.string().optional(),
|
||||
note: z.string().optional(),
|
||||
})),
|
||||
steps: z.array(z.object({
|
||||
instruction: z.string(),
|
||||
timerSeconds: z.number().int().optional(),
|
||||
})),
|
||||
});
|
||||
|
||||
export type ImportedRecipe = z.infer<typeof ImportedRecipeSchema>;
|
||||
|
||||
export async function importFromPhoto(
|
||||
imageBase64: string,
|
||||
mimeType: "image/jpeg" | "image/png" | "image/webp",
|
||||
config?: AiConfig,
|
||||
_locale?: string,
|
||||
): Promise<ImportedRecipe> {
|
||||
const model = resolveModel(config);
|
||||
|
||||
const { object } = await generateObject({
|
||||
model,
|
||||
schema: ImportedRecipeSchema,
|
||||
system:
|
||||
"You are a recipe extraction specialist. Extract the complete recipe from the provided image. Be precise with ingredient quantities and cooking instructions.",
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{ type: "image", image: imageBase64, mimeType },
|
||||
{ type: "text", text: "Extract the complete recipe from this image." },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return object;
|
||||
}
|
||||
Reference in New Issue
Block a user