---
title: "Use Document Authoring AI with Vercel AI SDK"
canonical_url: "https://www.nutrient.io/guides/document-authoring/ai/integrations/vercel-ai-sdk/"
md_url: "https://www.nutrient.io/guides/document-authoring/ai/integrations/vercel-ai-sdk.md"
last_updated: "2026-06-12T18:57:15.093Z"
description: "Connect Document Authoring AI tool definitions to Vercel AI SDK and execute tool calls in the browser editor."
---

# Vercel AI SDK integration

This page covers the Vercel-specific adapter that connects Document Authoring AI to [Vercel AI SDK](https://ai-sdk.dev/docs/introduction). For the conceptual model, see [agentic tools](https://www.nutrient.io/guides/document-authoring/ai/agentic-tools.md); for the Edit/Review/View policy, see [review and approval](https://www.nutrient.io/guides/document-authoring/ai/review-and-approval.md).

## Before you start

This guide assumes:

- A Document Authoring editor is already running in the browser.

- Your app uses Vercel AI SDK for the chat transport and the model loop.

- Your server can reach your model provider.

- The browser can grab the live [`DocAuthEditor`](https://www.nutrient.io/api/document-authoring/types/docautheditor/) instance when tool calls arrive.

The examples use OpenAI through Vercel AI SDK, but any Vercel-supported provider works the same way; the toolkit doesn’t care which model is on the other side.

## Server route for agentic tools

The toolkit ships framework-neutral helpers in `@nutrient-sdk/document-authoring-ai`. The Vercel adapter in `@nutrient-sdk/document-authoring-ai/vercel` converts those definitions into an AI SDK tool set before you hand them to [`streamText`](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text).

```ts

import { openai } from "@ai-sdk/openai";
import {
  convertToModelMessages,
  stepCountIs,
  streamText,
} from "ai";
import {
  getAiPromptGuide,
  getAiToolDefinitions,
} from "@nutrient-sdk/document-authoring-ai";
import { toVercelAiTools } from "@nutrient-sdk/document-authoring-ai/vercel";

const tools = toVercelAiTools(getAiToolDefinitions());
const system = `${getAiPromptGuide()}

When the user asks for motivation or justification, include a concise reviewComment on each write tool call.`;

export async function POST(req: Request) {
  const body = await req.json();

  const result = streamText({
    model: openai("gpt-4o-mini"),
    system,
    messages: await convertToModelMessages(body.messages),
    stopWhen: stepCountIs(20),
    temperature: 0.1,
    tools,
  });

  return result.toUIMessageStreamResponse();
}

```

The adapter returns tools with no `execute` handlers, so the route exposes the tools to the model but doesn’t run them. The browser does. `getAiPromptGuide` is the system prompt designed for these tools; using it as your base system prompt tells the model how the toolkit handles element IDs, read-before-write ordering, and the rest of the rules.

## Browser chat loop

The [`useChat`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) hook is where the model’s tool calls land in the browser. The toolkit work inside `onToolCall` is the same execution loop described in [agentic tools](https://www.nutrient.io/guides/document-authoring/ai/agentic-tools.md). The Vercel-specific part is feeding the result back through [`addToolOutput`](https://ai-sdk.dev/docs/ai-sdk-ui/chatbot-tool-usage).

```ts

import { useChat } from "@ai-sdk/react";
import {
  DefaultChatTransport,
  lastAssistantMessageIsCompleteWithToolCalls,
} from "ai";
import {
  isAiWriteToolName,
} from "@nutrient-sdk/document-authoring-ai";
import { getAiToolkit } from "@nutrient-sdk/document-authoring-ai/editor";

const toolkit = getAiToolkit(editor);

const { addToolOutput } = useChat({
  transport: new DefaultChatTransport({ api: "/api/chat" }),
  sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
  async onToolCall({ toolCall }) {
    // Skip Vercel dynamic tools; they aren't part of this route's tool set.
    if ("dynamic" in toolCall && toolCall.dynamic) {
      return;
    }

    const editorMode = editor.getEditorMode();
    const isWriteTool = isAiWriteToolName(toolCall.toolName);

    if (isWriteTool && editorMode === "view") {
      addToolOutput({
        tool: toolCall.toolName,
        toolCallId: toolCall.toolCallId,
        state: "output-error",
        errorText:
          "The document is in View mode. Switch to Edit or Review mode before editing.",
      });
      return;
    }

    const executed = await toolkit.executeTool(
      {
        id: toolCall.toolCallId,
        name: toolCall.toolName,
        args: toolCall.input,
      },
      {
        writeMode: isWriteTool && editorMode === "review"? "track_changes"
          : "apply",
        reviewComments: isWriteTool? "create" : "disabled",
      },
    );

    addToolOutput({
      tool: toolCall.toolName,
      toolCallId: toolCall.toolCallId,
      output: executed,
    });
  },
});

```

[`lastAssistantMessageIsCompleteWithToolCalls`](https://ai-sdk.dev/docs/ai-sdk-ui/chatbot-tool-usage) lets Vercel’s chat loop pick the conversation back up once you’ve submitted every tool output.

The `reviewComments` option is a host policy, not a model decision. The model can supply `reviewComment` on write tool calls because the tool schema exposes it, but the browser decides whether to create comment threads when it executes the call.

## Server route for workflows

Workflows use Vercel’s structured-output API instead of tool calls. The route receives workflow input from the browser and returns one validated output object with [`generateText`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text) and [`Output.object`](https://ai-sdk.dev/docs/reference/ai-sdk-core/output).

```ts

import { openai } from "@ai-sdk/openai";
import { generateText, Output } from "ai";
import { getBuiltInWorkflow } from "@nutrient-sdk/document-authoring-ai";
import { toVercelAiWorkflowOutputSchema } from "@nutrient-sdk/document-authoring-ai/vercel";

export async function POST(req: Request) {
  const body = await req.json();
  const workflow = getBuiltInWorkflow("proofreading");

  const result = await generateText({
    model: openai("gpt-4o-mini"),
    system: workflow.systemPrompt,
    prompt: JSON.stringify({
      task: body.task?? workflow.defaultTask,
      input: body.workflowInput,
    }),
    output: Output.object(toVercelAiWorkflowOutputSchema(workflow)),
    temperature: 0.1,
  });

  return Response.json({ output: result.output });
}

```

On the browser side, call `toolkit.readWorkflowInput` to build the request body, hit this route, and apply the result with `toolkit.applyWorkflowOutput`. The full browser flow is in [workflows](https://www.nutrient.io/guides/document-authoring/ai/workflows.md).
---

## Related pages

- [Convert document_authoring_ai["tools"] into the tool format expected by](/guides/document-authoring/ai/integrations/non-typescript-backends.md)
- [LangChain integration](/guides/document-authoring/ai/integrations/langchain.md)

