Tools
Adding new tools are a core functionality of the starterkit. This is very easy if you follow the next steps.
Creating A Tool
- Create a new file:
lib/ai/tools/new-tool-name.ts
- Make the tool :)
lib/ai/tools/new-tool-name.ts
import { tool } from 'ai';
import { z } from 'zod';
export const newToolName = tool({
description: 'Describe the tool to the AI model',
parameters: z.object({ // the parameters for this
parameterOne: z.number(),
parameterTwo: z.string(),
}),
execute: async ({ parameterOne, parameterTwo }) => {
return {
newString: `${parameterTwo}: ${parameterOne}$`
}
},
});
Now we need to give the AI access to the new tool
app/(chat)/api/chat/route.ts
return createDataStreamResponse({
execute: (dataStream) => {
const result = streamText({
model: myProvider.languageModel(selectedChatModel),
system: systemPrompt({ selectedChatModel }),
messages,
maxSteps: 5,
experimental_activeTools:
selectedChatModel === 'chat-model-reasoning'
? []
: [
'getWeather',
'createDocument',
'updateDocument',
'requestSuggestions',
'generateImageTool',
'getRandomWord',
'newToolName'
],
experimental_transform: smoothStream({ chunking: 'word' }),
experimental_generateMessageId: generateUUID,
tools: {
getWeather,
createDocument: createDocument({ userId: user.id, dataStream }),
updateDocument: updateDocument({ userId: user.id, dataStream }),
requestSuggestions: requestSuggestions({
userId: user.id,
dataStream,
}),
generateImageTool,
getRandomWord,
newToolName
},
onFinish: async ({ response, reasoning }) => {
posthog.capture('chat_response', {
'chat_id': id,
'model': selectedChatModel
})
if (user.id) {
try {
const sanitizedResponseMessages = sanitizeResponseMessages({
messages: response.messages,
reasoning,
});
await saveMessages({
messages: sanitizedResponseMessages.map((message) => {
return {
id: message.id,
chatId: id,
role: message.role,
content: message.content,
createdAt: new Date(),
};
}),
});
} catch (error) {
console.error('Failed to save chat');
}
}
},
experimental_telemetry: {
isEnabled: true,
functionId: 'stream-text',
},
});
result.consumeStream();
result.mergeIntoDataStream(dataStream, {
sendReasoning: true,
});
},
onError: () => {
return 'Oops, an error occured!';
},
});
the last step in the process is showing the data visually.
components/message.tsx
{/* Full Response Component */}
return (
<div key={toolCallId}>
{toolName === 'getWeather' ? (
<Weather weatherAtLocation={result} />
) : toolName === 'createDocument' ? (
<DocumentPreview
isReadonly={isReadonly}
result={result}
/>
) : toolName === 'updateDocument' ? (
<DocumentToolResult
type="update"
result={result}
isReadonly={isReadonly}
/>
) : toolName === 'requestSuggestions' ? (
<DocumentToolResult
type="request-suggestions"
result={result}
isReadonly={isReadonly}
/>
) : toolName === 'generateImageTool' ? (
<img src={result.imageUrl} className='w-full rounded-3xl' />
) : toolName === 'getRandomWord' ? (
<code>{result.word}</code>
) : toolName === 'newToolName' (
<p>result.newString</p>
) : (
<pre>{JSON.stringify(result, null, 2)}</pre>
)}
</div>
);
components/message.tsx
{/* Skeleton */}
{toolName === 'getWeather' ? (
<Weather />
) : toolName === 'createDocument' ? (
<DocumentPreview isReadonly={isReadonly} args={args} />
) : toolName === 'updateDocument' ? (
<DocumentToolCall
type="update"
args={args}
isReadonly={isReadonly}
/>
) : toolName === 'requestSuggestions' ? (
<DocumentToolCall
type="request-suggestions"
args={args}
isReadonly={isReadonly}
/>
) : toolName === 'generateImageTool' ? (
<ImageSkeleton args={args} />
) : toolName === 'getRandomWord' ? (
<code>Getting Random word</code>
) : toolName === 'newToolName' ? (
<p>Loading Results For New Tool</p>
) : null}
Now your tool should be available in the chat app. Be proud of your first tool, enjoy the dopamine rush.
Last updated on