Skip to main content

Building Simple Tools

Outline

Now that you understand how to define tools and their parameters, let's get hands-on by building a couple of simple, practical tools. These examples will solidify your understanding of the tool() decorator and parameter handling.

By the end of this module, you will be able to:

  • Build simple Opal tools that perform basic operations.

  • Implement conditional logic and optional parameters in tool functions.

  • Apply basic error handling and validation to make tools more reliable.

The "Greeting" Tool (Revisited and Enhanced)

We've already seen a basic version of this. Let's ensure it's fully functional and demonstrates good practices. This tool will take a person's name and an optional language, returning a personalized greeting.

Tool Definition (src/tools/GreetingTool.ts)

import { tool, ParameterType } from '@optimizely-opal/opal-tools-sdk';

interface GreetingParams {
    name: string;
    language?: 'en' | 'es' | 'fr'; // Enforce specific languages
    addExclamation?: boolean;
}

@tool({
    name: 'greet_person',
    description: 'Generates a personalized greeting for a person in a specified language.',
    parameters: [
        {
            name: 'name',
            type: ParameterType.String,
            description: 'The name of the person to greet.',
            required: true
        },
        {
            name: 'language',
            type: ParameterType.String,
            description: 'Optional language for the greeting (e.g., "en", "es", "fr"). Defaults to "en".',
            required: false,
            enum: ['en', 'es', 'fr'] // Restrict to these options
        },
        {
            name: 'addExclamation',
            type: ParameterType.Boolean,
            description: 'Set to true to add an exclamation mark to the greeting. Defaults to false.',
            required: false
        }
    ]
})
export class GreetingTool {
    async execute(params: GreetingParams) {
        const { name, language = 'en', addExclamation = false } = params;

        let greetingPhrase: string;
        switch (language) {
            case 'es':
                greetingPhrase = '¡Hola';
                break;
            case 'fr':
                greetingPhrase = 'Bonjour';
                break;
            case 'en':
            default:
                greetingPhrase = 'Hello';
                break;
        }

        const punctuation = addExclamation ? '!' : '.';

        return {
            greeting: `${greetingPhrase}, ${name}${punctuation}`,
            languageUsed: language
        };
    }
}

Integration (src/index.ts or src/main.ts): Remember to import and register your new tool in your main application file:

// ... other imports
import { GreetingTool } from './tools/GreetingTool'; // Adjust path as needed

const opalToolsService = new OpalToolsService();
opalToolsService.registerTool(GreetingTool);
// ... register other tools
app.use(opalToolsService.getExpressMiddleware());
// ... rest of your Express app setup

The "Current Date" Tool

This tool will return the current date and time, with an optional format parameter. This demonstrates handling optional parameters and basic date manipulation.

Tool Definition (src/tools/CurrentDateTool.ts):

import { tool, ParameterType } from '@optimizely-opal/opal-tools-sdk';

interface CurrentDateParams {
    format?: 'YYYY-MM-DD' | 'MM/DD/YYYY HH:mm' | 'full'; // Specific format options
    timezone?: string; // e.g., 'America/New_York'
}

@tool({
    name: 'get_current_date',
    description: 'Returns the current date and time, with optional formatting and timezone.',
    parameters: [
        {
            name: 'format',
            type: ParameterType.String,
            description: 'Optional format string. "YYYY-MM-DD", "MM/DD/YYYY HH:mm", or "full". Defaults to "full".',
            required: false,
            enum: ['YYYY-MM-DD', 'MM/DD/YYYY HH:mm', 'full']
        },
        {
            name: 'timezone',
            type: ParameterType.String,
            description: 'Optional IANA timezone string (e.g., "America/New_York"). Defaults to UTC.',
            required: false
        }
    ]
})
export class CurrentDateTool {
    async execute(params: CurrentDateParams) {
        const { format = 'full', timezone } = params;
        const now = new Date();

        let formattedDate: string;
        const options: Intl.DateTimeFormatOptions = {
            year: 'numeric',
            month: '2-digit',
            day: '2-digit',
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit',
            hour12: false, // Use 24-hour format
            timeZone: timezone || 'UTC'
        };

        switch (format) {
            case 'YYYY-MM-DD':
                formattedDate = new Intl.DateTimeFormat('en-CA', { // en-CA gives YYYY-MM-DD
                    year: 'numeric', month: '2-digit', day: '2-digit', timeZone: timezone || 'UTC'
                }).format(now);
                break;
            case 'MM/DD/YYYY HH:mm':
                formattedDate = new Intl.DateTimeFormat('en-US', {
                    year: 'numeric', month: '2-digit', day: '2-digit',
                    hour: '2-digit', minute: '2-digit', hour12: false, timeZone: timezone || 'UTC'
                }).format(now);
                break;
            case 'full':
            default:
                formattedDate = now.toLocaleString('en-US', options);
                break;
        }

        return {
            currentDateTime: formattedDate,
            timestampUTC: now.toISOString(), // Always provide ISO string for consistency
            requestedFormat: format,
            usedTimezone: timezone || 'UTC'
        };
    }
}

Integration (src/index.ts or src/main.ts):

// ... other imports
import { CurrentDateTool } from './tools/CurrentDateTool'; // Adjust path as needed

const opalToolsService = new OpalToolsService();
opalToolsService.registerTool(GreetingTool); // Already registered
opalToolsService.registerTool(CurrentDateTool); // Register the new tool
// ... rest of your Express app setup

Interactive Exercise: Temperature Converter Tool

Now it's your turn! Build a tool that converts temperatures between Celsius and Fahrenheit. This will involve taking a temperature (number) and a unit (string: "C" or "F") as parameters and returning the converted temperature along with the original and converted units.

  • Requirements:

    • Tool Name: convert_temperature
    • Description: Converts a temperature between Celsius and Fahrenheit.
    • Parameters:
      • temperature (Type: Number, Required: true, Description: "The temperature value to convert.")
      • unit (Type: String, Required: true, Description: "The unit of the input temperature ('C' for Celsius, 'F' for Fahrenheit).", Enum: ['C', 'F'])
    • Output: An object containing the original temperature and unit, and the converted temperature and unit.
    • Error Handling: If an invalid unit is provided, return an informative error message.
  • Hints:

    • Conversion formulas:
      • Celsius to Fahrenheit: (C * 9/5) + 32
      • Fahrenheit to Celsius: (F - 32) * 5/9
    • Use ParameterType.Number for the temperature.
    • Use ParameterType.String with an enum for the unit.
    • Implement try...catch or conditional logic to handle invalid unit inputs.

Take your time to implement this tool in a new file (e.g., src/tools/TemperatureConverterTool.ts) and register it in your main application file. Test it using your API testing tool (Postman/Insomnia) by sending requests to <a href="http://localhost:3000/tools/convert_temperature">http://localhost:3000/tools/convert_temperature</a>.