Documentation

AI

Direct SDK calls

Call the Vercel AI SDK directly in VitNode API routes. Resolve a model with c.get("ai").model(), then generate text, stream a response, get structured output with Output.object or create embeddings.

VitNode uses the Vercel AI SDK. Resolve a model from c.get("ai") and call the native SDK functions with it.

Building a feature users can trigger? Use an AI action

Calling generateText with c.get("ai").model() bypasses permissions, budgets, AI points and run history. Nobody watches the bill until it arrives. For user-facing features, define an AI action and run it with c.get("ai").run(). Keep direct SDK calls for internal tooling you fully control.

Request passing through c.get('ai') model registry and invoking AI SDK functions

Model resolvers

ResolverReturned typeUsed by
c.get("ai").model(id?)LanguageModelgenerateText, streamText
c.get("ai").embeddingModel(id?)EmbeddingModelembed, embedMany
c.get("ai").imageModel(id?)ImageModelgenerateImage

Without an id, each resolver returns the first model of its list in the AI config. The examples below use the ai package that VitNode Core depends on (AI SDK 7).


1. Text generation

Generate text in a staff-only API route. This one belongs to the site-notes plugin from the learning path; add it to the plugin's admin notes module like any other route:

plugins/site-notes/src/api/modules/admin/notes/summarize.route.ts
import { generateText } from 'ai'
import { z } from 'zod'

import { buildRoute } from '@/api/builders'

export const summarizeRoute = buildRoute({
  adminStaffPermission: { module: 'notes', permission: 'can_view' },
  route: {
    method: 'post',
    description: 'Summarize a text with the default AI model.',
    path: '/summarize',
    request: {
      body: {
        required: true,
        content: {
          'application/json': { schema: z.object({ text: z.string().min(1).max(20_000) }) },
        },
      },
    },
    responses: {
      200: {
        content: {
          'application/json': { schema: z.object({ summary: z.string() }) },
        },
        description: 'Text summary',
      },
    },
  },
  handler: async (c) => {
    const { text } = c.req.valid('json')

    const { text: summary } = await generateText({
      model: c.get('ai').model(),
      system: 'You are a concise summary assistant.',
      prompt: `Summarize the following content:\n\n${text}`,
    })

    return c.json({ summary }, 200)
  },
})

adminStaffPermission keeps the route to staff who hold notes → can_view. Swap in a permission your own plugin declares.


2. Streaming responses

Stream the model's answer to the client as plain text:

import { streamText } from 'ai'

handler: async (c) => {
  const result = streamText({
    model: c.get('ai').model(),
    prompt: 'Write an introduction to WebSockets.',
    abortSignal: c.req.raw.signal,
  })

  return result.toTextStreamResponse()
}

For a chat UI built on the AI SDK's useChat, return result.toUIMessageStreamResponse() instead. To stream a managed action with budgets and history, see Streaming.


3. Structured output

Get typed JSON back from the model by passing an output setting to generateText. The model must support structured output:

import { generateText, Output } from 'ai'
import { z } from 'zod'

const postMetadataSchema = z.object({
  title: z.string(),
  tags: z.array(z.string()),
  estimatedReadingMinutes: z.number(),
})

const { output } = await generateText({
  model: c.get('ai').model(),
  output: Output.object({ schema: postMetadataSchema }),
  prompt: 'Generate SEO metadata for an article on Postgres indexing.',
})

output is typed from the schema. generateObject still exists in AI SDK 7 but is deprecated in favour of this form.


4. Generating embeddings

Turn text into a vector embedding. This needs at least one entry in ai.embeddingModels, or embeddingModel() throws. See Multiple models and embeddings.

import { embed } from 'ai'

const { embedding } = await embed({
  model: c.get('ai').embeddingModel(),
  value: 'How to configure Redis caching in VitNode',
})

Learn more