Recipe: AI-powered
Turn free text into structured data with @civility/services. This is the “recipe importer” / “workout generator” shape: prompt + a Zod schema in, a typed object out. See the services guide for modes, grants, and managed credentials — here’s the minimal working call. Assume the singleton app store around this.
// www/utils/import.ts
import { z } from '@zod/zod'
import { generateStructured } from '@civility/services/ai'
import app from '../store/app.ts'
const Recipe = z.object({
title: z.string(),
ingredients: z.array(z.string()),
steps: z.array(z.string()),
servings: z.number().optional(),
})
export async function importRecipe(pastedText: string) {
const apiKey = app.settings.value?.aiApiKey
if (!apiKey) throw new Error('Add an AI key in Settings to use import.')
const recipe = await generateStructured({
prompt: 'Extract a structured recipe from the text the user pasted.',
userDescription: pastedText,
schema: Recipe, // result is validated + typed
config: { apiKey, provider: 'claude' },
})
await app.recipes.set(crypto.randomUUID(), { ...recipe, source: 'ai-import' })
return recipe
}
Wire it to a form: a textarea, a “Generate” button that calls importRecipe, and a settings input for the user’s key. Because the parsed result flows straight into a Collection, an AI-imported recipe is indistinguishable from a hand-entered one — it syncs, merges, and exports like any other record.
For the bring-your-own-key vs. host-bundle distinction (and handling services.quota_exceeded), see services §6.
Back to the app development guides.