AI agent chatbot
Build a simple AI agent chatbot that can read and edit Tiptap documents.
See the source code on GitHub.
Tech stack
- React + Next.js
- AI SDK by Vercel + OpenAI models
- Tiptap AI Toolkit
Installation
Create a Next.js project:
npx create-next-app@latest ai-agent-chatbotInstall the core Tiptap packages and the Vercel AI SDK for OpenAI:
npm install @tiptap/react @tiptap/starter-kit ai @ai-sdk/react @ai-sdk/openaiInstall the Tiptap AI Toolkit and the tool definitions for the Vercel AI SDK.
Pro package
The AI Toolkit is a pro package. Before installation, set up access to the private NPM registry by following the private registry guide.
npm install @tiptap-pro/ai-toolkit @tiptap-pro/ai-toolkit-ai-sdkAPI endpoint
Create an API endpoint that uses the Vercel AI SDK to call the OpenAI model. Include the tool definitions for the Tiptap AI Toolkit.
// app/api/chat/route.ts
import { openai } from '@ai-sdk/openai'
import { toolDefinitions } from '@tiptap-pro/ai-toolkit-ai-sdk'
import { createAgentUIStreamResponse, ToolLoopAgent, UIMessage } from 'ai'
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json()
const agent = new ToolLoopAgent({
model: openai('gpt-5-mini'),
instructions: 'You are an assistant that can edit rich text documents.',
tools: toolDefinitions(),
providerOptions: {
openai: {
reasoningEffort: 'minimal',
},
},
})
return createAgentUIStreamResponse({
agent,
uiMessages: messages,
})
}To access the OpenAI API, create an API key in the OpenAI Dashboard and add it as an environment variable. The environment variable will be detected automatically by the Vercel AI SDK.
# .env
OPENAI_API_KEY=your-api-keyWhen the AI model receives a request, it will decide to call the available tools to read the document and edit it. See the list of available tools for more details.
Client-side setup
When the AI model decides to call a tool, it needs to be executed on the client. This is done with the executeTool method:
import { Editor } from '@tiptap/react'
import { getAiToolkit } from '@tiptap-pro/ai-toolkit'
// Create a Tiptap Editor instance
const editor = new Editor()
// Get the Tiptap AI Toolkit instance
const toolkit = getAiToolkit(editor)
// The AI decides to call the `tiptapRead` tool to read the document
// Use the `executeTool` method to execute the tool and get the result
const result = toolkit.executeTool({
// The name of the tool to execute
toolName: 'tiptapRead',
// AI-generated input
input: {},
})
// Send the result back to the AI modelTo implement this in your app, create a client-side React component that renders the Tiptap Editor and a simple chat UI. This component leverages the useChat hook from the Vercel AI SDK to call the API endpoint and manage the chat conversation.
// app/page.tsx
'use client'
import { DefaultChatTransport, lastAssistantMessageIsCompleteWithToolCalls } from 'ai'
import { useChat } from '@ai-sdk/react'
import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { useState } from 'react'
import { AiToolkit, getAiToolkit } from '@tiptap-pro/ai-toolkit'
export default function Page() {
const editor = useEditor({
immediatelyRender: false,
extensions: [StarterKit, AiToolkit],
content: `<h1>AI agent demo</h1><p>Ask the AI to improve this.</p>`,
})
const { messages, sendMessage, addtoolOutput } = useChat({
transport: new DefaultChatTransport({ api: '/api/chat' }),
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
async onToolCall({ toolCall }) {
if (!editor) return
const { toolName, input, toolCallId } = toolCall
// Use the AI Toolkit to execute the tool
const toolkit = getAiToolkit(editor)
const result = toolkit.executeTool({
toolName,
input,
})
addtoolOutput({ tool: toolName, toolCallId, output: result.output })
},
})
const [input, setInput] = useState('Replace the last paragraph with a short story about Tiptap')
if (!editor) return null
return (
<div>
<EditorContent editor={editor} />
{messages?.map((message) => (
<div key={message.id} style={{ whiteSpace: 'pre-wrap' }}>
<strong>{message.role}</strong>
<br />
{message.parts
.filter((p) => p.type === 'text')
.map((p) => p.text)
.join('\n')}
</div>
))}
<form
onSubmit={(e) => {
e.preventDefault()
sendMessage({ text: input })
setInput('')
}}
>
<input value={input} onChange={(e) => setInput(e.target.value)} />
</form>
</div>
)
}End result
With additional CSS styles, the result is a simple but polished AI chatbot application:
See the source code on GitHub.
Next steps
- Let your users review AI-generated changes with the review changes guide
- Add real-time tool streaming to see changes as they happen with the tool streaming guide
- Does your document contain custom nodes and marks? Learn how make your AI agent understand them with the schema awareness guide.