Skip to content

Commit

Permalink
Handle int, float, array, \Stringable and \JsonSerializable
Browse files Browse the repository at this point in the history
… return values from tools (#161)
  • Loading branch information
OskarStark authored Dec 19, 2024
1 parent 75e1263 commit 8365e13
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 5 deletions.
12 changes: 11 additions & 1 deletion src/Chain/ToolBox/ToolBox.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,17 @@ public function execute(ToolCall $toolCall): string
foreach ($this->tools as $tool) {
foreach ($this->toolAnalyzer->getMetadata($tool::class) as $metadata) {
if ($metadata->name === $toolCall->name) {
return $tool->{$metadata->method}(...$toolCall->arguments);
$result = $tool->{$metadata->method}(...$toolCall->arguments);

if ($result instanceof \JsonSerializable || is_array($result)) {
return json_encode($result, flags: JSON_THROW_ON_ERROR);
}

if (is_integer($result) || is_float($result) || $result instanceof \Stringable) {
return (string) $result;
}

return $result;
}
}
}
Expand Down
100 changes: 96 additions & 4 deletions tests/Chain/ToolBox/ToolBoxTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolNoParams;
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolOptionalParam;
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolRequiredParams;
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolReturningArray;
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolReturningFloat;
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolReturningInteger;
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolReturningJsonSerializable;
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolReturningStringable;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
Expand All @@ -35,6 +40,11 @@ protected function setUp(): void
new ToolRequiredParams(),
new ToolOptionalParam(),
new ToolNoParams(),
new ToolReturningArray(),
new ToolReturningJsonSerializable(),
new ToolReturningInteger(),
new ToolReturningFloat(),
new ToolReturningStringable(),
]);
}

Expand Down Expand Up @@ -97,6 +107,41 @@ public function toolsMap(): void
'description' => 'A tool without parameters',
],
],
[
'type' => 'function',
'function' => [
'name' => 'tool_returning_array',
'description' => 'A tool returning an array',
],
],
[
'type' => 'function',
'function' => [
'name' => 'tool_returning_json_serializable',
'description' => 'A tool returning an object which implements \JsonSerializable',
],
],
[
'type' => 'function',
'function' => [
'name' => 'tool_returning_integer',
'description' => 'A tool returning an integer',
],
],
[
'type' => 'function',
'function' => [
'name' => 'tool_returning_float',
'description' => 'A tool returning a float',
],
],
[
'type' => 'function',
'function' => [
'name' => 'tool_returning_stringable',
'description' => 'A tool returning an object which implements \Stringable',
],
],
];

self::assertSame(json_encode($expected), json_encode($actual));
Expand All @@ -112,11 +157,58 @@ public function executeWithUnknownTool(): void
}

#[Test]
public function execute(): void
public function executeWithToolReturningString(): void
{
self::assertSame(
'Hello says "3".',
$this->toolBox->execute(
new ToolCall('call_1234', 'tool_required_params', ['text' => 'Hello', 'number' => 3])
)
);
}

#[Test]
public function executeWithToolReturningArray(): void
{
self::assertSame(
'{"foo":"bar"}',
$this->toolBox->execute(new ToolCall('call_1234', 'tool_returning_array'))
);
}

#[Test]
public function executeWithToolReturningJsonSerializable(): void
{
$actual = $this->toolBox->execute(new ToolCall('call_1234', 'tool_required_params', ['text' => 'Hello', 'number' => 3]));
$expected = 'Hello says "3".';
self::assertSame(
'{"foo":"bar"}',
$this->toolBox->execute(new ToolCall('call_1234', 'tool_returning_json_serializable'))
);
}

#[Test]
public function executeWithToolReturningInteger(): void
{
self::assertSame(
'42',
$this->toolBox->execute(new ToolCall('call_1234', 'tool_returning_integer'))
);
}

self::assertSame($expected, $actual);
#[Test]
public function executeWithToolReturningFloat(): void
{
self::assertSame(
'42.42',
$this->toolBox->execute(new ToolCall('call_1234', 'tool_returning_float'))
);
}

#[Test]
public function executeWithToolReturningStringable(): void
{
self::assertSame(
'Hi!',
$this->toolBox->execute(new ToolCall('call_1234', 'tool_returning_stringable'))
);
}
}
16 changes: 16 additions & 0 deletions tests/Fixture/Tool/ToolReturningArray.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Tests\Fixture\Tool;

use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool;

#[AsTool('tool_returning_array', 'A tool returning an array')]
final class ToolReturningArray
{
public function __invoke(): array
{
return ['foo' => 'bar'];
}
}
16 changes: 16 additions & 0 deletions tests/Fixture/Tool/ToolReturningFloat.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Tests\Fixture\Tool;

use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool;

#[AsTool('tool_returning_float', 'A tool returning a float')]
final class ToolReturningFloat
{
public function __invoke(): float
{
return 42.42;
}
}
16 changes: 16 additions & 0 deletions tests/Fixture/Tool/ToolReturningInteger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Tests\Fixture\Tool;

use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool;

#[AsTool('tool_returning_integer', 'A tool returning an integer')]
final class ToolReturningInteger
{
public function __invoke(): int
{
return 42;
}
}
21 changes: 21 additions & 0 deletions tests/Fixture/Tool/ToolReturningJsonSerializable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Tests\Fixture\Tool;

use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool;

#[AsTool('tool_returning_json_serializable', 'A tool returning an object which implements \JsonSerializable')]
final class ToolReturningJsonSerializable
{
public function __invoke(): \JsonSerializable
{
return new class implements \JsonSerializable {
public function jsonSerialize(): array
{
return ['foo' => 'bar'];
}
};
}
}
21 changes: 21 additions & 0 deletions tests/Fixture/Tool/ToolReturningStringable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Tests\Fixture\Tool;

use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool;

#[AsTool('tool_returning_stringable', 'A tool returning an object which implements \Stringable')]
final class ToolReturningStringable
{
public function __invoke(): \Stringable
{
return new class implements \Stringable {
public function __toString(): string
{
return 'Hi!';
}
};
}
}

0 comments on commit 8365e13

Please sign in to comment.