Tool Calling in AI SDK
Tool calling (also known as function calling) allows AI models to request execution of specific functions. This enables AI assistants to interact with external systems, fetch data, perform calculations, and take actions in the real world.
What Tools Enable
- Data Fetching: Get real-time weather, stock prices, user data
- Calculations: Perform complex math or data processing
- External APIs: Interact with third-party services
- Database Operations: Query and update data
- Actions: Send emails, create calendar events, etc.
Defining Tools
import { tool } from 'ai';
import { z } from 'zod';
// Define a weather tool
const weatherTool = tool({
description: 'Get the current weather for a location',
parameters: z.object({
location: z.string().describe('The city and state, e.g., San Francisco, CA'),
unit: z.enum(['celsius', 'fahrenheit']).optional().default('fahrenheit'),
}),
execute: async ({ location, unit }) => {
// Call your weather API here
const weather = await fetchWeather(location, unit);
return weather;
},
});
// Define a calculator tool
const calculatorTool = tool({
description: 'Perform mathematical calculations',
parameters: z.object({
expression: z.string().describe('The math expression to evaluate'),
}),
execute: async ({ expression }) => {
// Safely evaluate the expression
const result = evaluateMathExpression(expression);
return { result };
},
});
Using Tools with streamText
// app/api/chat/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText, tool } from 'ai';
import { z } from 'zod';
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4-turbo'),
messages,
tools: {
weather: tool({
description: 'Get the current weather for a location',
parameters: z.object({
location: z.string(),
}),
execute: async ({ location }) => {
// Simulate weather API call
return {
location,
temperature: 72,
condition: 'sunny',
};
},
}),
calculator: tool({
description: 'Calculate a math expression',
parameters: z.object({
expression: z.string(),
}),
execute: async ({ expression }) => {
return { result: eval(expression) }; // Use a safe evaluator in production
},
}),
},
});
return result.toDataStreamResponse();
}
Tool Results in Chat
'use client';
import { useChat } from 'ai/react';
export default function ChatWithTools() {
const { messages, input, handleInputChange, handleSubmit } = useChat();
return (
<div className="max-w-2xl mx-auto p-4">
{messages.map((message) => (
<div key={message.id} className="mb-4">
<strong>{message.role}:</strong>
{/* Display text content */}
{message.content && <p>{message.content}</p>}
{/* Display tool invocations */}
{message.toolInvocations?.map((tool, index) => (
<div key={index} className="bg-gray-100 p-2 rounded mt-2">
<p className="text-sm text-gray-600">
Called: {tool.toolName}
</p>
{tool.state === 'result' && (
<pre className="text-sm">
{JSON.stringify(tool.result, null, 2)}
</pre>
)}
</div>
))}
</div>
))}
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={handleInputChange}
placeholder="Ask about weather or calculate..."
className="w-full p-2 border rounded"
/>
</form>
</div>
);
}
Multi-Step Tool Calls
AI models can call multiple tools in sequence to complete complex tasks:
const result = streamText({
model: openai('gpt-4-turbo'),
messages,
tools: {
searchProducts: tool({
description: 'Search for products in the catalog',
parameters: z.object({
query: z.string(),
maxResults: z.number().optional().default(5),
}),
execute: async ({ query, maxResults }) => {
const products = await searchCatalog(query, maxResults);
return products;
},
}),
getProductDetails: tool({
description: 'Get detailed information about a product',
parameters: z.object({
productId: z.string(),
}),
execute: async ({ productId }) => {
const details = await getProduct(productId);
return details;
},
}),
addToCart: tool({
description: 'Add a product to the shopping cart',
parameters: z.object({
productId: z.string(),
quantity: z.number().default(1),
}),
execute: async ({ productId, quantity }) => {
await addToCart(productId, quantity);
return { success: true, message: 'Added to cart' };
},
}),
},
maxSteps: 5, // Allow up to 5 tool calls
});
Tool Choice Options
const result = streamText({
model: openai('gpt-4-turbo'),
messages,
tools: { weather, calculator },
// Let the model decide which tools to use (default)
toolChoice: 'auto',
// Force the model to use a specific tool
// toolChoice: { type: 'tool', toolName: 'weather' },
// Require the model to use at least one tool
// toolChoice: 'required',
// Prevent tool usage
// toolChoice: 'none',
});
Handling Tool Errors
const weatherTool = tool({
description: 'Get current weather',
parameters: z.object({
location: z.string(),
}),
execute: async ({ location }) => {
try {
const response = await fetch(
`https://api.weather.com/${location}`
);
if (!response.ok) {
// Return error message for the AI to handle
return {
error: true,
message: `Could not get weather for ${location}`,
};
}
return await response.json();
} catch (error) {
return {
error: true,
message: 'Weather service is unavailable',
};
}
},
});
Server Action with Tools
// app/actions.ts
'use server';
import { openai } from '@ai-sdk/openai';
import { generateText, tool } from 'ai';
import { z } from 'zod';
export async function askWithTools(prompt: string) {
const { text, toolResults } = await generateText({
model: openai('gpt-4-turbo'),
prompt,
tools: {
getCurrentTime: tool({
description: 'Get the current time',
parameters: z.object({
timezone: z.string().optional().default('UTC'),
}),
execute: async ({ timezone }) => {
return new Date().toLocaleString('en-US', { timeZone: timezone });
},
}),
},
});
return { text, toolResults };
}
Real-World Example: AI Assistant
// app/api/assistant/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText, tool } from 'ai';
import { z } from 'zod';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4-turbo'),
system: `You are a helpful assistant with access to tools.
Use tools when they would help answer the user's question.
Always explain what you're doing when using tools.`,
messages,
tools: {
searchWeb: tool({
description: 'Search the web for current information',
parameters: z.object({
query: z.string(),
}),
execute: async ({ query }) => {
// Integrate with search API
return await searchWeb(query);
},
}),
createReminder: tool({
description: 'Create a reminder for the user',
parameters: z.object({
title: z.string(),
datetime: z.string(),
priority: z.enum(['low', 'medium', 'high']).optional(),
}),
execute: async ({ title, datetime, priority }) => {
await createReminder({ title, datetime, priority });
return { success: true, message: `Reminder "${title}" created` };
},
}),
sendEmail: tool({
description: 'Send an email on behalf of the user',
parameters: z.object({
to: z.string().email(),
subject: z.string(),
body: z.string(),
}),
execute: async ({ to, subject, body }) => {
await sendEmail({ to, subject, body });
return { success: true, message: `Email sent to ${to}` };
},
}),
},
maxSteps: 3,
});
return result.toDataStreamResponse();
}
Security Considerations
- • Validate all tool inputs thoroughly using Zod schemas
- • Never trust AI-generated code or expressions (avoid eval)
- • Implement proper authorization for sensitive tools
- • Rate limit tool executions to prevent abuse
- • Log tool usage for auditing and debugging
Key Takeaways
- • Tools extend AI capabilities with real-world actions
- • Define tools with clear descriptions and Zod schemas
- • Use
maxStepsfor multi-step tool interactions - • Handle tool errors gracefully for better UX
- • Access
toolInvocationsin messages to display results
Learn More
-
Tool Calling Documentation →
Complete guide to tools in AI SDK Core.
-
useChat with Tools →
Integrating tools with the chat UI hook.