-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'lg/groq' of https://github.com/vercel/ai into lg/groq
- Loading branch information
Showing
20 changed files
with
420 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
content/examples/02-next-pages/11-state-management/01-save-messages.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
--- | ||
title: Save Messages | ||
description: Learn to save messages in your Next.js application using the AI SDK | ||
--- | ||
|
||
# Save Messages | ||
|
||
You can save the chat to an external database using the `onFinish` callback handler present in the `streamText` function. | ||
|
||
```ts filename="@/app/api/chat/route.ts" | ||
import { openai } from '@ai-sdk/openai'; | ||
import { saveChat } from '@/utils/queries'; | ||
import { streamText, convertToCoreMessages } from 'ai'; | ||
|
||
export function POST(request) { | ||
const { id, messages } = await request.json(); | ||
|
||
const coreMessages = convertToCoreMessages(messages); | ||
|
||
const result = await streamText({ | ||
model: openai('gpt-4o'), | ||
system: 'you are a friendly assistant!', | ||
messages: coreMessages, | ||
onFinish: async ({ responseMessages }) => { | ||
try { | ||
await saveChat({ | ||
id, | ||
messages: [...coreMessages, ...responseMessages], | ||
}); | ||
} catch (error) { | ||
console.error('Failed to save chat'); | ||
} | ||
}, | ||
}); | ||
|
||
return result.toDataStream(); | ||
} | ||
``` | ||
|
||
The `saveChat` function is a server action that saves the chat to the database. It first checks if the chat already exists in the database and updates the chat if it does. If the chat does not exist, it inserts a new chat into the database. | ||
|
||
<Note> | ||
This example assumes that the database table schema has a `messages` column that stores the chat messages as a JSON string of type `Array<CoreMessage>`. You can modify the `saveChat` function to suit your database schema. | ||
</Note> | ||
|
||
```ts filename="@/app/utils/queries.ts" | ||
export async function saveChat({ | ||
id, | ||
messages, | ||
userId, | ||
}: { | ||
id: string; | ||
messages: any; | ||
userId: string; | ||
}) { | ||
try { | ||
const selectedChats = await db.select().from(chat).where(eq(chat.id, id)); | ||
|
||
if (selectedChats.length > 0) { | ||
return await db | ||
.update(chat) | ||
.set({ | ||
messages: JSON.stringify(messages), | ||
}) | ||
.where(eq(chat.id, id)); | ||
} | ||
|
||
return await db.insert(chat).values({ | ||
id, | ||
createdAt: new Date(), | ||
messages: JSON.stringify(messages), | ||
userId, | ||
}); | ||
} catch (error) { | ||
console.error('Failed to save chat in database'); | ||
throw error; | ||
} | ||
} | ||
``` |
67 changes: 67 additions & 0 deletions
67
content/examples/02-next-pages/11-state-management/02-restore-messages.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
--- | ||
title: Restore Messages | ||
description: Learn to restore chat in your Next.js application using the AI SDK | ||
--- | ||
|
||
# Restore Messages | ||
|
||
The `useChat` hook accepts `initialMessages` to load an earlier chat history of messages. | ||
|
||
As messages are typically loaded from an external database, we can use server actions inside the Page component to fetch an already existing chat from the database during static generation and pass the messages as props to the `<Chat/>` component. | ||
|
||
<Note> | ||
This example assumes that the database table schema has a `messages` column that stores the chat messages as a JSON string of type `Array<CoreMessage>`. You can modify the `convertToUIMessages` function to suit your database schema, but here's an [example implementation](https://github.com/vercel/ai-chatbot/blob/00b125378c998d19ef60b73fe576df0fe5a0e9d4/lib/utils.ts). | ||
</Note> | ||
|
||
```tsx filename="@/app/chat/[id]/page.tsx" | ||
import { Chat } from '@/app/components/chat'; | ||
import { getChatById } from '@/utils/queries'; | ||
import { convertToUIMessages } from '@/utils/functions'; | ||
|
||
export default async function Page({ params }: { params: any }) { | ||
const { id } = params; | ||
const chatFromDb = await getChatById({ id }); | ||
|
||
const chat: Chat = { | ||
...chatFromDb, | ||
messages: convertToUIMessages(chatFromDb.messages), | ||
}; | ||
|
||
return <Chat key={id} id={chat.id} initialMessages={chat.messages} />; | ||
} | ||
``` | ||
|
||
In the above code snippet, we fetch the chat from the database using the `getChatById` server action and convert the messages to the format expected by `useChat` component using the `convertToUIMessages` function. | ||
|
||
```tsx filename="@/app/components/chat.tsx" | ||
'use client'; | ||
|
||
import { Message } from 'ai'; | ||
import { useChat } from 'ai/react'; | ||
|
||
export function Chat({ | ||
id, | ||
initialMessages, | ||
}: { | ||
id; | ||
initialMessages: Array<Message>; | ||
}) { | ||
const { messages } = useChat({ | ||
id, | ||
initialMessages, | ||
}); | ||
|
||
return ( | ||
<div> | ||
{messages.map(message => ( | ||
<div key={message.id}> | ||
<div>{message.role}</div> | ||
<div>{message.content}</div> | ||
</div> | ||
))} | ||
</div> | ||
); | ||
} | ||
``` | ||
|
||
In the `Chat` component, we pass the `initialMessages` prop to the `useChat` hook to load the chat history. The `useChat` hook will then return the messages to be rendered in the chat window. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
title: State Management | ||
description: Learn to manage state in your Next.js application using the AI SDK | ||
--- | ||
|
||
In this section you will learn to manage state in your Next.js application using the AI SDK. |
118 changes: 118 additions & 0 deletions
118
content/examples/02-next-pages/12-interface/01-route-components.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
--- | ||
title: Route Components | ||
description: Learn to route components using the AI SDK in your Next.js App Router application | ||
--- | ||
|
||
Language models can call external tools to enhance responses beyond plain text. For weather queries, a React weather component could replace a text-only answer. This approach enables richer, interactive outputs. By combining AI language understanding with custom UI elements, we can create more useful and engaging interfaces. The key is thoughtfully matching user needs to appropriate visual or interactive components. | ||
|
||
Let's create a weather tool that fetches weather data for a given location and return the result as props for our React component. | ||
|
||
```ts filename="@/app/api/chat/route.ts" | ||
import { z } from 'zod'; | ||
import { openai } from '@ai-sdk/openai'; | ||
import { getWeather } from '@/utils/queries'; | ||
import { streamText, convertToCoreMessages } from 'ai'; | ||
|
||
export function POST(request) { | ||
const { messages } = await request.json(); | ||
|
||
const coreMessages = convertToCoreMessages(messages); | ||
|
||
const result = await streamText({ | ||
model: openai('gpt-4o'), | ||
system: 'you are a friendly assistant!', | ||
messages: coreMessages, | ||
tools: { | ||
displayWeather: { | ||
description: 'Display the weather for a location', | ||
parameters: z.object({ | ||
latitude: z.number(), | ||
longitude: z.number(), | ||
}), | ||
execute: async function ({ latitude, longitude }) { | ||
const props = await getWeather({ latitude, longitude }); | ||
return props; | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
return result.toDataStream(); | ||
} | ||
``` | ||
|
||
Now we can render the React component that uses the weather data returned by the `displayWeather` tool. | ||
|
||
```tsx filename="@/components/chat.tsx" | ||
'use client'; | ||
|
||
import { useChat } from 'ai/react'; | ||
import { Weather } from '@/components/weather'; | ||
|
||
export default function Chat() { | ||
const { messages, input, setInput, handleSubmit } = useChat(); | ||
|
||
return ( | ||
<div> | ||
{messages.map(message => ( | ||
<div key={message.id}> | ||
<div>{message.role}</div> | ||
<div>{message.content}</div> | ||
|
||
<div> | ||
{message.toolInvocations.map(toolInvocation => { | ||
const { toolName, toolCallId, state } = toolInvocation; | ||
|
||
if (state === 'result') { | ||
const { result } = toolInvocation; | ||
|
||
return ( | ||
<div key={toolCallId}> | ||
{toolName === 'displayWeather' ? ( | ||
<Weather weatherAtLocation={result} /> | ||
) : null} | ||
</div> | ||
); | ||
} else { | ||
return ( | ||
<div key={toolCallId}> | ||
{toolName === 'displayWeather' ? ( | ||
<div>Loading weather...</div> | ||
) : null} | ||
</div> | ||
); | ||
} | ||
})} | ||
</div> | ||
</div> | ||
))} | ||
|
||
<form onSubmit={handleSubmit}> | ||
<input | ||
type="text" | ||
value={input} | ||
onChange={event => { | ||
setInput(event.target.value); | ||
}} | ||
/> | ||
<button type="submit">Send</button> | ||
</form> | ||
</div> | ||
); | ||
} | ||
``` | ||
|
||
The `Weather` component can be a simple React component that displays the weather data. | ||
|
||
```tsx filename="@/components/weather.tsx" | ||
import React from 'react'; | ||
|
||
export function Weather({ weatherAtLocation }) { | ||
const { value, unit } = weatherAtLocation; | ||
return ( | ||
<div> | ||
{value}°{unit} | ||
</div> | ||
); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
title: Generative User Interface | ||
description: earn to build generative user interfaces using the AI SDK in your Next.js application | ||
--- | ||
|
||
In this section you will learn to use the tool calling ability of language models to integrate more than just text responses to your application. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { openai } from '@ai-sdk/openai'; | ||
import { CoreToolCallUnion, CoreToolResultUnion, generateText, tool } from 'ai'; | ||
import { z } from 'zod'; | ||
|
||
const myToolSet = { | ||
firstTool: tool({ | ||
description: 'Greets the user', | ||
parameters: z.object({ name: z.string() }), | ||
execute: async ({ name }) => `Hello, ${name}!`, | ||
}), | ||
secondTool: tool({ | ||
description: 'Tells the user their age', | ||
parameters: z.object({ age: z.number() }), | ||
execute: async ({ age }) => `You are ${age} years old!`, | ||
}), | ||
}; | ||
|
||
type MyToolCall = CoreToolCallUnion<typeof myToolSet>; | ||
type MyToolResult = CoreToolResultUnion<typeof myToolSet>; | ||
|
||
async function generateSomething(prompt: string): Promise<{ | ||
text: string; | ||
toolCalls: Array<MyToolCall>; // typed tool calls | ||
toolResults: Array<MyToolResult>; // typed tool results | ||
}> { | ||
return generateText({ | ||
model: openai('gpt-4o'), | ||
tools: myToolSet, | ||
prompt, | ||
}); | ||
} | ||
|
||
const { text, toolCalls, toolResults } = await generateSomething('...'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.