feat: add docs (#760)

This commit is contained in:
Jeremy 2025-02-05 14:58:38 +03:00 committed by GitHub
parent 9c4dbc8aaa
commit cf4ab9a4a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 326 additions and 0 deletions

40
docs/01-quick-start.md Normal file
View file

@ -0,0 +1,40 @@
# Quick Start
The chatbot template is a web application built using [Next.js](https://nextjs.org) and the [AI SDK](https://sdk.vercel.ai) that can be used as a starting point for building your own AI applications. The template is designed to be easily customizable and extendable, allowing you to add new features and integrations as needed.
Deploying to [Vercel](https://vercel.com) is the quickest way to get started with the chatbot template, as it automatically sets up the project by connecting to integrations and deploys it to the cloud. You can then later develop the project locally and push changes to the Vercel project.
### Pre-requisites:
- Vercel account and [Vercel CLI](https://vercel.com/docs/cli)
- GitHub/GitLab/Bitbucket account
- API Key from [OpenAI](https://platform.openai.com)
### Deploy to Vercel
To deploy the chatbot template to Vercel, click this [link](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fai-chatbot&env=AUTH_SECRET,OPENAI_API_KEY&envDescription=Learn%20more%20about%20how%20to%20get%20the%20API%20Keys%20for%20the%20application&envLink=https%3A%2F%2Fgithub.com%2Fvercel%2Fai-chatbot%2Fblob%2Fmain%2F.env.example&demo-title=AI%20Chatbot&demo-description=An%20Open-Source%20AI%20Chatbot%20Template%20Built%20With%20Next.js%20and%20the%20AI%20SDK%20by%20Vercel.&demo-url=https%3A%2F%2Fchat.vercel.ai&stores=%5B%7B%22type%22:%22postgres%22%7D,%7B%22type%22:%22blob%22%7D%5D) to enter the 1-click deploy flow.
During the flow, you will be prompted to create and connect to a postgres database and blob store. You will also need to provide environment variables for the application.
After deploying the project, you can access the chatbot template by visiting the URL provided by Vercel.
### Local Development
To develop the chatbot template locally, you can clone the repository and link it to your Vercel project. This will allow you to pull the environment variables from the Vercel project and use them locally.
```bash
git clone https://github.com/<username>/<repository>
cd <repository>
pnpm install
vercel link
vercel env pull
```
After linking the project, you can start the development server by running:
```bash
pnpm dev
```
The chatbot template will be available at `http://localhost:3000`.

53
docs/02-update-models.md Normal file
View file

@ -0,0 +1,53 @@
# Update Models
The chatbot template ships with [OpenAI](https://sdk.vercel.ai/providers/ai-sdk-providers/openai) as the default model provider. Since the template is powered by the [AI SDK](https://sdk.vercel.ai), which supports [multiple providers](https://sdk.vercel.ai/providers/ai-sdk-providers) out of the box, you can easily switch to another provider of your choice.
To update the models, you will need to update the custom provider called `myProvider` at `/lib/ai/models.ts` shown below.
```ts
import { customProvider } from "ai";
import { openai } from "@ai-sdk/openai";
export const myProvider = customProvider({
languageModels: {
"chat-model-small": openai("gpt-4o-mini"),
"chat-model-large": openai("gpt-4o"),
"chat-model-reasoning": wrapLanguageModel({
model: fireworks("accounts/fireworks/models/deepseek-r1"),
middleware: extractReasoningMiddleware({ tagName: "think" }),
}),
"title-model": openai("gpt-4-turbo"),
"block-model": openai("gpt-4o-mini"),
},
imageModels: {
"small-model": openai.image("dall-e-3"),
},
});
```
You can replace the `openai` models with any other provider of your choice. You will need to install the provider library and switch the models accordingly.
For example, if you want to use Anthropic's `claude-3-5-sonnet` model for `chat-model-large`, you can replace the `openai` model with the `anthropic` model as shown below.
```ts
import { customProvider } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
export const myProvider = customProvider({
languageModels: {
"chat-model-small": openai("gpt-4o-mini"),
"chat-model-large": anthropic("claude-3-5-sonnet"), // Replace openai with anthropic
"chat-model-reasoning": wrapLanguageModel({
model: fireworks("accounts/fireworks/models/deepseek-r1"),
middleware: extractReasoningMiddleware({ tagName: "think" }),
}),
"title-model": openai("gpt-4-turbo"),
"block-model": openai("gpt-4o-mini"),
},
imageModels: {
"small-model": openai.image("dall-e-3"),
},
});
```
You can find the provider library and model names in the [provider](https://sdk.vercel.ai/providers/ai-sdk-providers)'s documentation. Once you have updated the models, you should be able to use the new models in your chatbot.

233
docs/03-blocks.md Normal file
View file

@ -0,0 +1,233 @@
# Blocks
Blocks is a special user interface mode that allows you to have a workspace like interface along with the chat interface. This is similar to [ChatGPT's Canvas](https://openai.com/index/introducing-canvas) and [Claude's Artifacts](https://www.anthropic.com/news/artifacts).
The template already ships with the following blocks:
- **Text Block**: A block that allows you to work with text content like drafting essays and emails.
- **Code Block**: A block that allows you to write and execute code (Python).
- **Image Block**: A block that allows you to work with images like editing, annotating, and processing images.
- **Sheet Block**: A block that allows you to work with tabular data like creating, editing, and analyzing data.
## Adding a Custom Block
To add a custom block, you will need to create a folder in the `blocks` directory with the block name. The folder should contain the following files:
- `client.tsx`: The client-side code for the block.
- `server.ts`: The server-side code for the block.
Here is an example of a custom block called `CustomBlock`:
```bash
blocks/
custom/
client.tsx
server.ts
```
### Client-Side Example (client.tsx)
This file is responsible for rendering your custom block. You might replace the inner UI with your own components, but the overall pattern (initialization, handling streamed data, and rendering content) remains the same. For instance:
```tsx
import { Block } from "@/components/create-block";
import { ExampleComponent } from "@/components/example-component";
import { toast } from "sonner";
interface CustomBlockMetadata {
// Define metadata your custom block might need—the example below is minimal.
info: string;
}
export const customBlock = new Block<"custom", CustomBlockMetadata>({
kind: "custom",
description: "A custom block for demonstrating custom functionality.",
// Initialization can fetch any extra data or perform side effects
initialize: async ({ documentId, setMetadata }) => {
// For example, initialize the block with default metadata.
setMetadata({
info: `Document ${documentId} initialized.`,
});
},
// Handle streamed parts from the server (if your block supports streaming updates)
onStreamPart: ({ streamPart, setMetadata, setBlock }) => {
if (streamPart.type === "info-update") {
setMetadata((metadata) => ({
...metadata,
info: streamPart.content as string,
}));
}
if (streamPart.type === "content-update") {
setBlock((draftBlock) => ({
...draftBlock,
content: draftBlock.content + (streamPart.content as string),
status: "streaming",
}));
}
},
// Defines how the block content is rendered
content: ({
mode,
status,
content,
isCurrentVersion,
currentVersionIndex,
onSaveContent,
getDocumentContentById,
isLoading,
metadata,
}) => {
if (isLoading) {
return <div>Loading custom block...</div>;
}
if (mode === "diff") {
const oldContent = getDocumentContentById(currentVersionIndex - 1);
const newContent = getDocumentContentById(currentVersionIndex);
return (
<div>
<h3>Diff View</h3>
<pre>{oldContent}</pre>
<pre>{newContent}</pre>
</div>
);
}
return (
<div className="custom-block">
<ExampleComponent
content={content}
metadata={metadata}
onSaveContent={onSaveContent}
isCurrentVersion={isCurrentVersion}
/>
<button
onClick={() => {
navigator.clipboard.writeText(content);
toast.success("Content copied to clipboard!");
}}
>
Copy
</button>
</div>
);
},
// An optional set of actions exposed in the block toolbar.
actions: [
{
icon: <span></span>,
description: "Refresh block info",
onClick: ({ appendMessage }) => {
appendMessage({
role: "user",
content: "Please refresh the info for my custom block.",
});
},
},
],
// Additional toolbar actions for more control
toolbar: [
{
icon: <span></span>,
description: "Edit custom block",
onClick: ({ appendMessage }) => {
appendMessage({
role: "user",
content: "Edit the custom block content.",
});
},
},
],
});
```
Server-Side Example (server.ts)
The server file processes the document for the block. It streams updates (if applicable) and returns the final content. For example:
```ts
import { smoothStream, streamText } from "ai";
import { myProvider } from "@/lib/ai/models";
import { createDocumentHandler } from "@/lib/blocks/server";
import { updateDocumentPrompt } from "@/lib/ai/prompts";
export const customDocumentHandler = createDocumentHandler<"custom">({
kind: "custom",
// Called when the document is first created.
onCreateDocument: async ({ title, dataStream }) => {
let draftContent = "";
// For demonstration, use streamText to generate content.
const { fullStream } = streamText({
model: myProvider.languageModel("block-model"),
system:
"Generate a creative piece based on the title. Markdown is supported.",
experimental_transform: smoothStream({ chunking: "word" }),
prompt: title,
});
// Stream the content back to the client.
for await (const delta of fullStream) {
if (delta.type === "text-delta") {
draftContent += delta.textDelta;
dataStream.writeData({
type: "content-update",
content: delta.textDelta,
});
}
}
return draftContent;
},
// Called when updating the document based on user modifications.
onUpdateDocument: async ({ document, description, dataStream }) => {
let draftContent = "";
const { fullStream } = streamText({
model: myProvider.languageModel("block-model"),
system: updateDocumentPrompt(document.content, "custom"),
experimental_transform: smoothStream({ chunking: "word" }),
prompt: description,
experimental_providerMetadata: {
openai: {
prediction: {
type: "content",
content: document.content,
},
},
},
});
for await (const delta of fullStream) {
if (delta.type === "text-delta") {
draftContent += delta.textDelta;
dataStream.writeData({
type: "content-update",
content: delta.textDelta,
});
}
}
return draftContent;
},
});
```
Once you have created the client and server files, you can import the block in the `lib/blocks/server.ts` file and add it to the `documentHandlersByBlockKind` array.
```ts
export const documentHandlersByBlockKind: Array<DocumentHandler> = [
...,
customDocumentHandler,
];
export const blockKinds = [..., "custom"] as const;
```
And also add the client-side block to the `blockDefinitions` array in the `components/block.tsx` file.
```ts
import { customBlock } from "@/blocks/custom/client";
export const blockDefinitions = [..., customBlock];
```
You should now be able to see the custom block in the workspace!