diff --git a/src/AgentExecutor/Interceptor/GeneratePromptInterceptor.php b/src/AgentExecutor/Interceptor/GeneratePromptInterceptor.php index 2c3daca..8b152b8 100644 --- a/src/AgentExecutor/Interceptor/GeneratePromptInterceptor.php +++ b/src/AgentExecutor/Interceptor/GeneratePromptInterceptor.php @@ -12,6 +12,9 @@ use LLM\Agents\LLM\AgentPromptGeneratorInterface; use LLM\Agents\LLM\Prompt\Chat\PromptInterface; +/** + * This interceptor is responsible for generating the prompt for the agent. + */ final readonly class GeneratePromptInterceptor implements ExecutorInterceptorInterface { public function __construct( diff --git a/src/AgentExecutor/Interceptor/InjectModelInterceptor.php b/src/AgentExecutor/Interceptor/InjectModelInterceptor.php index 23a97be..c181e05 100644 --- a/src/AgentExecutor/Interceptor/InjectModelInterceptor.php +++ b/src/AgentExecutor/Interceptor/InjectModelInterceptor.php @@ -10,6 +10,9 @@ use LLM\Agents\AgentExecutor\ExecutorInterceptorInterface; use LLM\Agents\AgentExecutor\InterceptorHandler; +/** + * This interceptor is responsible for injecting the model name into the execution options. + */ final readonly class InjectModelInterceptor implements ExecutorInterceptorInterface { public function __construct( diff --git a/src/AgentExecutor/Interceptor/ToolExecutorInterceptor.php b/src/AgentExecutor/Interceptor/ToolExecutorInterceptor.php new file mode 100644 index 0000000..e473eeb --- /dev/null +++ b/src/AgentExecutor/Interceptor/ToolExecutorInterceptor.php @@ -0,0 +1,75 @@ +options->get('return_tool_result', false); + + while (true) { + $result = $execution->result; + $prompt = $execution->prompt; + + // If the result is a ToolCalledResponse, we need to call the tools. + if ($result instanceof ToolCalledResponse) { + // First, call all tools. + $toolsResponse = []; + foreach ($result->tools as $tool) { + $toolsResponse[] = $this->callTool($tool); + } + + // In some cases we want to return the tool result instead of adding it to the prompt. + // We don't need an answer from the LLM, all we wanted is to ask LLM to execute desired tools. + if ($shouldReturnToolResult) { + return new Execution( + result: new ToolsCallResultResponse(results: $toolsResponse), + prompt: $prompt, + ); + } + + // Then add the tools responses to the prompt. + foreach ($toolsResponse as $toolResponse) { + $input = $input->withPrompt($prompt->withAddedMessage($toolResponse)); + } + + // Continue to the next execution. + $execution = $next($input); + + continue; + } + + return $execution; + } + } + + private function callTool(ToolCall $tool): ToolCallResultMessage + { + $functionResult = $this->toolExecutor->execute($tool->name, $tool->arguments); + + return new ToolCallResultMessage( + id: $tool->id, + content: [$functionResult], + ); + } +} diff --git a/src/LLM/Prompt/Chat/ToolsCallResultResponse.php b/src/LLM/Prompt/Chat/ToolsCallResultResponse.php new file mode 100644 index 0000000..81b7148 --- /dev/null +++ b/src/LLM/Prompt/Chat/ToolsCallResultResponse.php @@ -0,0 +1,29 @@ + $results + */ + public function __construct( + public array $results, + ) { + parent::__construct(''); + } + + public function jsonSerialize(): array + { + return [ + 'results' => \array_map( + static fn(ToolCallResultMessage $result): array => $result->toArray(), + $this->results, + ), + ]; + } +} diff --git a/src/Tool/ToolChoice.php b/src/Tool/ToolChoice.php new file mode 100644 index 0000000..87e3d9a --- /dev/null +++ b/src/Tool/ToolChoice.php @@ -0,0 +1,71 @@ +type === ToolChoiceType::Auto; + } + + public function isAny(): bool + { + return $this->type === ToolChoiceType::Any; + } + + public function isSpecific(): bool + { + return $this->type === ToolChoiceType::Specific; + } + + public function isNone(): bool + { + return $this->type === ToolChoiceType::None; + } +} diff --git a/src/Tool/ToolChoiceType.php b/src/Tool/ToolChoiceType.php new file mode 100644 index 0000000..80aae7a --- /dev/null +++ b/src/Tool/ToolChoiceType.php @@ -0,0 +1,20 @@ +