TechLead
Lesson 6 of 8
6 min read
AI SDK

Tool Calling & Function Calling

Enable AI models to call functions and use tools for enhanced capabilities

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 maxSteps for multi-step tool interactions
  • • Handle tool errors gracefully for better UX
  • • Access toolInvocations in messages to display results

Learn More

Continue Learning