Skip to main content

Overview

The defineTool() function defines tools (functions) that language models can call during generation. Tools enable models to access external data, perform calculations, or trigger actions beyond text generation.

Function Signature

export function defineTool(
    options: ToolDefinition
): ToolDefinition

type ToolDefinition = {
    name: string;
    description: string;
    parameters: z.ZodType;
};

Parameters

name
string
required
The unique name of the tool. This is what the model uses to identify and call the tool.Should be descriptive and follow snake_case convention (e.g., get_weather, search_database).
description
string
required
A clear description of what the tool does. This helps the model understand when and how to use it.Should explain the tool’s purpose, expected inputs, and what it returns.
parameters
z.ZodType
required
Zod schema defining the tool’s input parameters. The model will generate arguments matching this schema.

Return Value

Returns the same ToolDefinition object that was passed in. This is a type-safe helper that provides IDE autocompletion and validation.

Examples

Basic Tool Definition

import { defineTool } from '@coreai/core';
import { z } from 'zod';

const weatherTool = defineTool({
  name: 'get_weather',
  description: 'Get the current weather for a location',
  parameters: z.object({
    location: z.string().describe('City name or zip code')
  })
});

Tool with Multiple Parameters

const searchTool = defineTool({
  name: 'search_database',
  description: 'Search the product database with filters',
  parameters: z.object({
    query: z.string().describe('Search query'),
    category: z.string().optional().describe('Product category filter'),
    maxResults: z.number().min(1).max(100).default(10)
      .describe('Maximum number of results to return')
  })
});

Using Tools with generate()

import { generate, defineTool } from '@coreai/core';
import { openai } from '@coreai/openai';
import { z } from 'zod';

const calculatorTool = defineTool({
  name: 'calculate',
  description: 'Perform mathematical calculations',
  parameters: z.object({
    expression: z.string().describe('Math expression to evaluate')
  })
});

const result = await generate({
  model: openai('gpt-4'),
  messages: [
    { role: 'user', content: 'What is 142 * 89?' }
  ],
  tools: {
    calculate: calculatorTool
  }
});

if (result.toolCalls.length > 0) {
  const toolCall = result.toolCalls[0];
  console.log('Tool:', toolCall.name);
  console.log('Arguments:', toolCall.arguments);
  // Output: { expression: '142 * 89' }
}

Tool Execution Loop

const tools = {
  get_weather: defineTool({
    name: 'get_weather',
    description: 'Get weather for a location',
    parameters: z.object({
      location: z.string()
    })
  }),
  get_time: defineTool({
    name: 'get_time',
    description: 'Get current time in a timezone',
    parameters: z.object({
      timezone: z.string().describe('IANA timezone name')
    })
  })
};

// Implement tool handlers
const toolHandlers = {
  get_weather: async (args: { location: string }) => {
    // Call weather API
    return `The weather in ${args.location} is sunny, 72°F`;
  },
  get_time: async (args: { timezone: string }) => {
    const time = new Date().toLocaleString('en-US', { 
      timeZone: args.timezone 
    });
    return `Current time in ${args.timezone}: ${time}`;
  }
};

const messages = [
  { role: 'user' as const, content: 'What\'s the weather in Tokyo?' }
];

let result = await generate({
  model: openai('gpt-4'),
  messages,
  tools
});

// Execute tools and continue conversation
while (result.toolCalls.length > 0) {
  for (const toolCall of result.toolCalls) {
    const handler = toolHandlers[toolCall.name as keyof typeof toolHandlers];
    const toolResult = await handler(toolCall.arguments as any);
    
    messages.push({
      role: 'assistant',
      parts: result.parts
    });
    
    messages.push({
      role: 'tool',
      toolCallId: toolCall.id,
      content: toolResult
    });
  }
  
  result = await generate({
    model: openai('gpt-4'),
    messages,
    tools
  });
}

console.log(result.content);
// Output: "The weather in Tokyo is sunny, 72°F"

Complex Parameters with Nested Objects

const createEventTool = defineTool({
  name: 'create_calendar_event',
  description: 'Create a new calendar event',
  parameters: z.object({
    title: z.string().describe('Event title'),
    startTime: z.string().datetime().describe('ISO 8601 start time'),
    endTime: z.string().datetime().describe('ISO 8601 end time'),
    attendees: z.array(
      z.object({
        name: z.string(),
        email: z.string().email()
      })
    ).optional().describe('List of attendees'),
    location: z.string().optional(),
    reminders: z.object({
      email: z.boolean().default(true),
      popup: z.number().min(0).optional()
        .describe('Minutes before event for popup')
    }).optional()
  })
});

Tool with Enum Parameters

const analyzeSentimentTool = defineTool({
  name: 'analyze_sentiment',
  description: 'Analyze the sentiment of text',
  parameters: z.object({
    text: z.string().describe('Text to analyze'),
    language: z.enum(['en', 'es', 'fr', 'de'])
      .default('en')
      .describe('Language of the text'),
    includeScore: z.boolean()
      .default(false)
      .describe('Include numerical sentiment score')
  })
});

Multiple Tools in One Call

const tools = {
  search_web: defineTool({
    name: 'search_web',
    description: 'Search the web for information',
    parameters: z.object({
      query: z.string()
    })
  }),
  
  get_stock_price: defineTool({
    name: 'get_stock_price',
    description: 'Get current stock price',
    parameters: z.object({
      symbol: z.string().describe('Stock ticker symbol')
    })
  }),
  
  convert_currency: defineTool({
    name: 'convert_currency',
    description: 'Convert between currencies',
    parameters: z.object({
      amount: z.number(),
      from: z.string().describe('Source currency code'),
      to: z.string().describe('Target currency code')
    })
  })
};

const result = await generate({
  model: openai('gpt-4'),
  messages: [
    { role: 'user', content: 'What\'s Tesla\'s stock price in euros?' }
  ],
  tools
});

// Model might call get_stock_price then convert_currency

Controlling Tool Usage

// Auto: Model decides whether to use tools
const result1 = await generate({
  model: openai('gpt-4'),
  messages: [{ role: 'user', content: 'Hello' }],
  tools: { get_weather: weatherTool },
  toolChoice: 'auto' // Default
});

// Required: Model must use a tool
const result2 = await generate({
  model: openai('gpt-4'),
  messages: [{ role: 'user', content: 'Get weather' }],
  tools: { get_weather: weatherTool },
  toolChoice: 'required'
});

// Force specific tool
const result3 = await generate({
  model: openai('gpt-4'),
  messages: [{ role: 'user', content: 'Get weather' }],
  tools: { 
    get_weather: weatherTool,
    get_time: timeTool
  },
  toolChoice: { type: 'tool', toolName: 'get_weather' }
});

// None: Disable tools
const result4 = await generate({
  model: openai('gpt-4'),
  messages: [{ role: 'user', content: 'Hello' }],
  tools: { get_weather: weatherTool },
  toolChoice: 'none'
});

Type Safety

The tool definition is fully type-safe:
const userTool = defineTool({
  name: 'get_user',
  description: 'Get user information',
  parameters: z.object({
    userId: z.number(),
    includeProfile: z.boolean().optional()
  })
});

// TypeScript knows the parameter types
type ToolParams = z.infer<typeof userTool.parameters>;
// { userId: number; includeProfile?: boolean | undefined }

const handler = async (args: ToolParams) => {
  const userId: number = args.userId; // Type-safe
  const include: boolean | undefined = args.includeProfile; // Type-safe
  // Fetch and return user data
};

Best Practices

Use descriptive tool names that clearly indicate their purpose. Follow the snake_case convention.
Write clear descriptions that explain what the tool does, when to use it, and what it returns. Good descriptions improve model accuracy.
Use Zod’s .describe() method on parameters to provide additional context to the model.
Always validate tool results before passing them back to the model. Handle errors gracefully.

Common Patterns

1. Database Queries

const queryTool = defineTool({
  name: 'query_database',
  description: 'Query the database for records',
  parameters: z.object({
    table: z.enum(['users', 'products', 'orders']),
    filters: z.record(z.unknown()).optional()
  })
});

2. API Calls

const apiTool = defineTool({
  name: 'call_api',
  description: 'Make an HTTP API request',
  parameters: z.object({
    endpoint: z.string(),
    method: z.enum(['GET', 'POST', 'PUT', 'DELETE']),
    body: z.record(z.unknown()).optional()
  })
});

3. File Operations

const fileTool = defineTool({
  name: 'read_file',
  description: 'Read contents of a file',
  parameters: z.object({
    path: z.string(),
    encoding: z.enum(['utf8', 'base64']).default('utf8')
  })
});

Source Location

~/workspace/source/packages/core-ai/src/tool.ts:4