-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19 from llm-agents-php/feature/tool-choise
Working with tools
- Loading branch information
Showing
6 changed files
with
201 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace LLM\Agents\AgentExecutor\Interceptor; | ||
|
||
use LLM\Agents\Agent\Execution; | ||
use LLM\Agents\AgentExecutor\ExecutionInput; | ||
use LLM\Agents\AgentExecutor\ExecutorInterceptorInterface; | ||
use LLM\Agents\AgentExecutor\InterceptorHandler; | ||
use LLM\Agents\LLM\Prompt\Chat\ToolCallResultMessage; | ||
use LLM\Agents\LLM\Prompt\Chat\ToolsCallResultResponse; | ||
use LLM\Agents\LLM\Response\ToolCall; | ||
use LLM\Agents\LLM\Response\ToolCalledResponse; | ||
use LLM\Agents\Tool\ToolExecutor; | ||
|
||
final readonly class ToolExecutorInterceptor implements ExecutorInterceptorInterface | ||
{ | ||
public function __construct( | ||
private ToolExecutor $toolExecutor, | ||
) {} | ||
|
||
public function execute(ExecutionInput $input, InterceptorHandler $next): Execution | ||
{ | ||
$execution = $next($input); | ||
|
||
// Check if we should return the tool result instead of adding it to the prompt. | ||
$shouldReturnToolResult = $input->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], | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace LLM\Agents\LLM\Prompt\Chat; | ||
|
||
use LLM\Agents\LLM\Response\Response; | ||
|
||
final class ToolsCallResultResponse extends Response implements \JsonSerializable | ||
{ | ||
/** | ||
* @param array<ToolCallResultMessage> $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, | ||
), | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace LLM\Agents\Tool; | ||
|
||
/** | ||
* In some cases, you may want LLM to use a specific tool to answer the user’s question, even if LLM thinks it can | ||
* provide an answer without using a tool. | ||
* | ||
* This class allows you to instruct LLM to have a specific behavior when it comes to using tools. | ||
*/ | ||
readonly class ToolChoice | ||
{ | ||
private function __construct( | ||
public ToolChoiceType $type, | ||
public ?string $toolName = null, | ||
) {} | ||
|
||
/** | ||
* Let LLM decide which function to call or not to call at all | ||
*/ | ||
public static function auto(): self | ||
{ | ||
return new self(ToolChoiceType::Auto); | ||
} | ||
|
||
/** | ||
* Force LLM to always call one or more functions | ||
*/ | ||
public static function any(): self | ||
{ | ||
return new self(ToolChoiceType::Any); | ||
} | ||
|
||
/** | ||
* Force LLM to call a specific function with the given name | ||
*/ | ||
public static function specific(string $toolName): self | ||
{ | ||
return new self(ToolChoiceType::Specific, $toolName); | ||
} | ||
|
||
/** | ||
* Force LLM not to call any function | ||
*/ | ||
public static function none(): self | ||
{ | ||
return new self(ToolChoiceType::None); | ||
} | ||
|
||
public function isAuto(): bool | ||
{ | ||
return $this->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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace LLM\Agents\Tool; | ||
|
||
enum ToolChoiceType | ||
{ | ||
// Let LLM decide which function to call or not to call at all | ||
case Auto; | ||
|
||
// Force LLM to always call one or more functions | ||
case Any; | ||
|
||
// Force LLM to call a specific function with the given name | ||
case Specific; | ||
|
||
// Force LLM not to call any function | ||
case None; | ||
} |