-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: introduce optional fault tolerant toolbox
- Loading branch information
1 parent
706497c
commit 57bad21
Showing
15 changed files
with
229 additions
and
67 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace PhpLlm\LlmChain\Chain\ToolBox\Exception; | ||
|
||
use PhpLlm\LlmChain\Exception\ExceptionInterface as BaseExceptionInterface; | ||
|
||
interface ExceptionInterface extends BaseExceptionInterface | ||
{ | ||
} |
21 changes: 21 additions & 0 deletions
21
src/Chain/ToolBox/Exception/ToolConfigurationException.php
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,21 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace PhpLlm\LlmChain\Chain\ToolBox\Exception; | ||
|
||
use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool; | ||
use PhpLlm\LlmChain\Exception\InvalidArgumentException; | ||
|
||
final class ToolConfigurationException extends InvalidArgumentException implements ExceptionInterface | ||
{ | ||
public static function missingAttribute(string $className): self | ||
{ | ||
return new self(sprintf('The class "%s" is not a tool, please add %s attribute.', $className, AsTool::class)); | ||
} | ||
|
||
public static function invalidMethod(string $toolClass, string $methodName): self | ||
{ | ||
return new self(sprintf('Method "%s" not found in tool "%s".', $methodName, $toolClass)); | ||
} | ||
} |
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 PhpLlm\LlmChain\Chain\ToolBox\Exception; | ||
|
||
use PhpLlm\LlmChain\Model\Response\ToolCall; | ||
|
||
final class ToolExecutionException extends \RuntimeException implements ExceptionInterface | ||
{ | ||
public ?ToolCall $toolCall = null; | ||
|
||
public static function executionFailed(ToolCall $toolCall, \Throwable $previous): self | ||
{ | ||
$exception = new self(sprintf('Execution of tool "%s" failed with error: %s', $toolCall->name, $previous->getMessage()), previous: $previous); | ||
$exception->toolCall = $toolCall; | ||
|
||
return $exception; | ||
} | ||
} |
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 PhpLlm\LlmChain\Chain\ToolBox\Exception; | ||
|
||
use PhpLlm\LlmChain\Model\Response\ToolCall; | ||
|
||
final class ToolNotFoundException extends \RuntimeException implements ExceptionInterface | ||
{ | ||
public ?ToolCall $toolCall = null; | ||
|
||
public static function notFoundForToolCall(ToolCall $toolCall): self | ||
{ | ||
$exception = new self(sprintf('Tool not found for call: %s.', $toolCall->name)); | ||
$exception->toolCall = $toolCall; | ||
|
||
return $exception; | ||
} | ||
} |
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,38 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace PhpLlm\LlmChain\Chain\ToolBox; | ||
|
||
use PhpLlm\LlmChain\Chain\ToolBox\Exception\ToolExecutionException; | ||
use PhpLlm\LlmChain\Chain\ToolBox\Exception\ToolNotFoundException; | ||
use PhpLlm\LlmChain\Model\Response\ToolCall; | ||
|
||
/** | ||
* Catches exceptions thrown by the inner tool box and returns error messages for the LLM instead. | ||
*/ | ||
final readonly class FaultTolerantToolBox implements ToolBoxInterface | ||
{ | ||
public function __construct( | ||
private ToolBoxInterface $innerToolBox, | ||
) { | ||
} | ||
|
||
public function getMap(): array | ||
{ | ||
return $this->innerToolBox->getMap(); | ||
} | ||
|
||
public function execute(ToolCall $toolCall): mixed | ||
{ | ||
try { | ||
return $this->innerToolBox->execute($toolCall); | ||
} catch (ToolExecutionException $e) { | ||
return sprintf('An error occurred while executing tool "%s".', $e->toolCall->name); | ||
} catch (ToolNotFoundException) { | ||
$names = array_map(fn (Metadata $metadata) => $metadata->name, $this->getMap()); | ||
|
||
return sprintf('Tool "%s" was not found, please use one of these: %s', $toolCall->name, implode(', ', $names)); | ||
} | ||
} | ||
} |
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
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 was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
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,78 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace PhpLlm\LlmChain\Tests\Chain\ToolBox; | ||
|
||
use PhpLlm\LlmChain\Chain\ToolBox\Exception\ToolExecutionException; | ||
use PhpLlm\LlmChain\Chain\ToolBox\Exception\ToolNotFoundException; | ||
use PhpLlm\LlmChain\Chain\ToolBox\FaultTolerantToolBox; | ||
use PhpLlm\LlmChain\Chain\ToolBox\Metadata; | ||
use PhpLlm\LlmChain\Chain\ToolBox\ToolBoxInterface; | ||
use PhpLlm\LlmChain\Model\Response\ToolCall; | ||
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolNoParams; | ||
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolRequiredParams; | ||
use PHPUnit\Framework\Attributes\CoversClass; | ||
use PHPUnit\Framework\Attributes\Test; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
#[CoversClass(FaultTolerantToolBox::class)] | ||
final class FaultTolerantToolBoxTest extends TestCase | ||
{ | ||
#[Test] | ||
public function faultyToolExecution(): void | ||
{ | ||
$faultyToolBox = $this->createFaultyToolBox( | ||
fn (ToolCall $toolCall) => ToolExecutionException::executionFailed($toolCall, new \Exception('error')) | ||
); | ||
|
||
$faultTolerantToolBox = new FaultTolerantToolBox($faultyToolBox); | ||
$expected = 'An error occurred while executing tool "tool_foo".'; | ||
|
||
$toolCall = new ToolCall('987654321', 'tool_foo'); | ||
$actual = $faultTolerantToolBox->execute($toolCall); | ||
|
||
self::assertSame($expected, $actual); | ||
} | ||
|
||
#[Test] | ||
public function faultyToolCall(): void | ||
{ | ||
$faultyToolBox = $this->createFaultyToolBox( | ||
fn (ToolCall $toolCall) => ToolNotFoundException::notFoundForToolCall($toolCall) | ||
); | ||
|
||
$faultTolerantToolBox = new FaultTolerantToolBox($faultyToolBox); | ||
$expected = 'Tool "tool_xyz" was not found, please use one of these: tool_no_params, tool_required_params'; | ||
|
||
$toolCall = new ToolCall('123456789', 'tool_xyz'); | ||
$actual = $faultTolerantToolBox->execute($toolCall); | ||
|
||
self::assertSame($expected, $actual); | ||
} | ||
|
||
private function createFaultyToolBox(\Closure $exceptionFactory): ToolBoxInterface | ||
{ | ||
return new class($exceptionFactory) implements ToolBoxInterface { | ||
public function __construct(private readonly \Closure $exceptionFactory) | ||
{ | ||
} | ||
|
||
/** | ||
* @return Metadata[] | ||
*/ | ||
public function getMap(): array | ||
{ | ||
return [ | ||
new Metadata(ToolNoParams::class, 'tool_no_params', 'A tool without parameters', '__invoke', null), | ||
new Metadata(ToolRequiredParams::class, 'tool_required_params', 'A tool with required parameters', 'bar', null), | ||
]; | ||
} | ||
|
||
public function execute(ToolCall $toolCall): mixed | ||
{ | ||
throw ($this->exceptionFactory)($toolCall); | ||
} | ||
}; | ||
} | ||
} |
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
Oops, something went wrong.