-
Notifications
You must be signed in to change notification settings - Fork 0
Tool Executor
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
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}');
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());
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
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
This diagram illustrates the relationships between the main ToolExecutor
class and its related classes, showing how they interact to provide the tool execution functionality.