Skip to content

Tool Executor

Pavel Buchnev edited this page Sep 19, 2024 · 2 revisions

Feature Explanation

The Tool Executor is a crucial component in the LLM Agents system, designed to execute tools based on their programming language and input. It acts as a central hub for managing and executing various tools that can be used by AI agents.

The main class, ToolExecutor, uses a strategy pattern to delegate execution to language-specific executors. It supports multiple programming languages through a flexible registration system and provides a unified interface for tool execution.

Key functionalities include:

  • Registering language-specific executors
  • Executing tools with given inputs
  • Handling tool retrieval and input schema mapping
  • Error handling and result formatting

Use Cases (5 Examples)

a) Executing a PHP tool to check website availability:

class WebsiteChecker extends PhpTool
{
    public function __construct(
        public readonly string $name = 'check_website',
        public readonly string $inputSchema = CheckWebsiteInput::class,
        public readonly string $description = 'Check if a website is online',
    ) {}

    public function execute(object $input): string
    {
        $ch = curl_init($input->url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return json_encode(['status' => $httpCode, 'online' => $httpCode >= 200 && $httpCode < 300]);
    }
}

$toolExecutor->execute(name: 'check_website', input: '{"url": "https://example.com"}');

b) Using a Python tool for data analysis:

class DataAnalyzer extends Tool implements LanguageExecutorAwareInterface
{
    public function __construct(
        public readonly string $name = 'analyze_data',
        public readonly string $inputSchema = AnalyzeDataInput::class,
        public readonly string $description = 'Analyze data using Python',
    ) {}

    public function getLanguage(): ToolLanguage
    {
        return ToolLanguage::Python;
    }

    public function getExecutableCode(): string
    {
        return <<<'PYTHON'
import pandas as pd
import json

def analyze_data(data):
    df = pd.DataFrame(json.loads(data))
    return json.dumps({
        'mean': df.mean().to_dict(),
        'median': df.median().to_dict(),
        'std': df.std().to_dict()
    })
PYTHON;
    }
}

$toolExecutor->execute(name: 'analyze_data', input: '{"data": [{"x": 1, "y": 2}, {"x": 3, "y": 4}]}');

c) Implementing a JavaScript tool for text processing:

class TextProcessor extends Tool implements LanguageExecutorAwareInterface
{
    public function __construct(
        public readonly string $name = 'process_text',
        public readonly string $inputSchema = ProcessTextInput::class,
        public readonly string $description = 'Process text using JavaScript',
    ) {}

    public function getLanguage(): ToolLanguage
    {
        return ToolLanguage::JavaScript;
    }

    public function getExecutableCode(): string
    {
        return <<<'JAVASCRIPT'
function processText(input) {
    const { text } = JSON.parse(input);
    const wordCount = text.split(/\s+/).length;
    const charCount = text.length;
    return JSON.stringify({ wordCount, charCount });
}
JAVASCRIPT;
    }
}

$toolExecutor->execute(name: 'process_text', input: '{"text": "Hello, world! This is a test."}');

d) Creating a Ruby tool for generating random data:

class RandomDataGenerator extends Tool implements LanguageExecutorAwareInterface
{
    public function __construct(
        public readonly string $name = 'generate_random_data',
        public readonly string $inputSchema = GenerateRandomDataInput::class,
        public readonly string $description = 'Generate random data using Ruby',
    ) {}

    public function getLanguage(): ToolLanguage
    {
        return ToolLanguage::Ruby;
    }

    public function getExecutableCode(): string
    {
        return <<<'RUBY'
require 'json'

def generate_random_data(input)
  count = JSON.parse(input)['count']
  data = count.times.map do
    {
      id: rand(1000),
      name: ('A'..'Z').to_a.sample(8).join,
      value: rand * 100
    }
  end
  JSON.generate(data)
end
RUBY;
    }
}

$toolExecutor->execute(name: 'generate_random_data', input: '{"count": 5}');

e) Implementing a Lua tool for simple calculations:

class Calculator extends Tool implements LanguageExecutorAwareInterface
{
    public function __construct(
        public readonly string $name = 'calculate',
        public readonly string $inputSchema = CalculateInput::class,
        public readonly string $description = 'Perform calculations using Lua',
    ) {}

    public function getLanguage(): ToolLanguage
    {
        return ToolLanguage::Lua;
    }

    public function getExecutableCode(): string
    {
        return <<<'LUA'
function calculate(input)
    local data = json.decode(input)
    local result = {
        sum = data.a + data.b,
        difference = data.a - data.b,
        product = data.a * data.b,
        quotient = data.a / data.b
    }
    return json.encode(result)
end
LUA;
    }
}

$toolExecutor->execute(name: 'calculate', input: '{"a": 10, "b": 5}');

Configuration Options

The ToolExecutor class doesn't have many configuration options directly, but it relies on the following components that can be configured:

a) ToolRepositoryInterface:

  • Default: Not set, must be provided in the constructor
  • Purpose: Manages the collection of available tools
  • Configuration:
$toolRepository = new ToolRegistry();
$toolExecutor = new ToolExecutor(toolRepository: $toolRepository, schemaMapper: $schemaMapper);

b) SchemaMapperInterface:

  • Default: Not set, must be provided in the constructor
  • Purpose: Handles conversion between JSON schemas and PHP objects
  • Configuration:
$schemaMapper = new SchemaMapper(generator: new JsonSchemaGenerator(), mapper: new TreeMapper());
$toolExecutor = new ToolExecutor(toolRepository: $toolRepository, schemaMapper: $schemaMapper);

c) Language Executors:

  • Default: Only PHP executor is registered by default
  • Purpose: Execute tools in specific programming languages
  • Configuration:
$toolExecutor->registerLanguageExecutor(language: ToolLanguage::Python, executor: new PythonLanguageExecutor());
$toolExecutor->registerLanguageExecutor(language: ToolLanguage::JavaScript, executor: new JavaScriptLanguageExecutor());

Related Classes

a) ToolInterface:

  • Purpose: Defines the contract for all tools
  • Functionality: Specifies methods for getting tool name, description, input schema, and language

b) ToolRepositoryInterface:

  • Purpose: Manages the collection of available tools
  • Functionality: Provides methods to retrieve tools by name and check for their existence

c) SchemaMapperInterface:

  • Purpose: Handles conversion between JSON schemas and PHP objects
  • Functionality: Converts input JSON to PHP objects and vice versa

d) LanguageExecutorInterface:

  • Purpose: Defines the contract for language-specific executors
  • Functionality: Executes tool code in a specific programming language

e) ToolLanguage (Enum):

  • Purpose: Defines supported programming languages for tools
  • Functionality: Provides a mapping between language names and their corresponding MIME types

Mermaid Class Diagram

classDiagram
    class ToolExecutor {
        -ToolRepositoryInterface toolRepository
        -SchemaMapperInterface schemaMapper
        -array languageExecutors
        +registerLanguageExecutor(ToolLanguage, LanguageExecutorInterface)
        +execute(string, string) string|Stringable
    }
    class ToolInterface {
        <<interface>>
        +getName() string
        +getDescription() string
        +getInputSchema() string
        +getLanguage() ToolLanguage
    }
    class ToolRepositoryInterface {
        <<interface>>
        +get(string) ToolInterface
        +has(string) bool
    }
    class SchemaMapperInterface {
        <<interface>>
        +toJsonSchema(string) array
        +toObject(string, string) object
    }
    class LanguageExecutorInterface {
        <<interface>>
        +execute(ToolInterface, object) string|Stringable
    }
    class ToolLanguage {
        <<enumeration>>
        PHP
        Lua
        Python
        Ruby
        JavaScript
        TypeScript
        Shell
    }
    ToolExecutor --> ToolRepositoryInterface
    ToolExecutor --> SchemaMapperInterface
    ToolExecutor --> "0..*" LanguageExecutorInterface
    ToolExecutor ..> ToolInterface
    ToolInterface ..> ToolLanguage
Loading

This diagram illustrates the relationships between the main ToolExecutor class and its related classes, showing how they interact to provide the tool execution functionality.