Skip to content
This repository was archived by the owner on Jul 16, 2025. It is now read-only.

Commit 8365e13

Browse files
authored
Handle int, float, array, \Stringable and \JsonSerializable return values from tools (#161)
1 parent 75e1263 commit 8365e13

File tree

7 files changed

+197
-5
lines changed

7 files changed

+197
-5
lines changed

src/Chain/ToolBox/ToolBox.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,17 @@ public function execute(ToolCall $toolCall): string
5050
foreach ($this->tools as $tool) {
5151
foreach ($this->toolAnalyzer->getMetadata($tool::class) as $metadata) {
5252
if ($metadata->name === $toolCall->name) {
53-
return $tool->{$metadata->method}(...$toolCall->arguments);
53+
$result = $tool->{$metadata->method}(...$toolCall->arguments);
54+
55+
if ($result instanceof \JsonSerializable || is_array($result)) {
56+
return json_encode($result, flags: JSON_THROW_ON_ERROR);
57+
}
58+
59+
if (is_integer($result) || is_float($result) || $result instanceof \Stringable) {
60+
return (string) $result;
61+
}
62+
63+
return $result;
5464
}
5565
}
5666
}

tests/Chain/ToolBox/ToolBoxTest.php

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolNoParams;
1515
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolOptionalParam;
1616
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolRequiredParams;
17+
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolReturningArray;
18+
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolReturningFloat;
19+
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolReturningInteger;
20+
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolReturningJsonSerializable;
21+
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolReturningStringable;
1722
use PHPUnit\Framework\Attributes\CoversClass;
1823
use PHPUnit\Framework\Attributes\Test;
1924
use PHPUnit\Framework\Attributes\UsesClass;
@@ -35,6 +40,11 @@ protected function setUp(): void
3540
new ToolRequiredParams(),
3641
new ToolOptionalParam(),
3742
new ToolNoParams(),
43+
new ToolReturningArray(),
44+
new ToolReturningJsonSerializable(),
45+
new ToolReturningInteger(),
46+
new ToolReturningFloat(),
47+
new ToolReturningStringable(),
3848
]);
3949
}
4050

@@ -97,6 +107,41 @@ public function toolsMap(): void
97107
'description' => 'A tool without parameters',
98108
],
99109
],
110+
[
111+
'type' => 'function',
112+
'function' => [
113+
'name' => 'tool_returning_array',
114+
'description' => 'A tool returning an array',
115+
],
116+
],
117+
[
118+
'type' => 'function',
119+
'function' => [
120+
'name' => 'tool_returning_json_serializable',
121+
'description' => 'A tool returning an object which implements \JsonSerializable',
122+
],
123+
],
124+
[
125+
'type' => 'function',
126+
'function' => [
127+
'name' => 'tool_returning_integer',
128+
'description' => 'A tool returning an integer',
129+
],
130+
],
131+
[
132+
'type' => 'function',
133+
'function' => [
134+
'name' => 'tool_returning_float',
135+
'description' => 'A tool returning a float',
136+
],
137+
],
138+
[
139+
'type' => 'function',
140+
'function' => [
141+
'name' => 'tool_returning_stringable',
142+
'description' => 'A tool returning an object which implements \Stringable',
143+
],
144+
],
100145
];
101146

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

114159
#[Test]
115-
public function execute(): void
160+
public function executeWithToolReturningString(): void
161+
{
162+
self::assertSame(
163+
'Hello says "3".',
164+
$this->toolBox->execute(
165+
new ToolCall('call_1234', 'tool_required_params', ['text' => 'Hello', 'number' => 3])
166+
)
167+
);
168+
}
169+
170+
#[Test]
171+
public function executeWithToolReturningArray(): void
172+
{
173+
self::assertSame(
174+
'{"foo":"bar"}',
175+
$this->toolBox->execute(new ToolCall('call_1234', 'tool_returning_array'))
176+
);
177+
}
178+
179+
#[Test]
180+
public function executeWithToolReturningJsonSerializable(): void
116181
{
117-
$actual = $this->toolBox->execute(new ToolCall('call_1234', 'tool_required_params', ['text' => 'Hello', 'number' => 3]));
118-
$expected = 'Hello says "3".';
182+
self::assertSame(
183+
'{"foo":"bar"}',
184+
$this->toolBox->execute(new ToolCall('call_1234', 'tool_returning_json_serializable'))
185+
);
186+
}
187+
188+
#[Test]
189+
public function executeWithToolReturningInteger(): void
190+
{
191+
self::assertSame(
192+
'42',
193+
$this->toolBox->execute(new ToolCall('call_1234', 'tool_returning_integer'))
194+
);
195+
}
119196

120-
self::assertSame($expected, $actual);
197+
#[Test]
198+
public function executeWithToolReturningFloat(): void
199+
{
200+
self::assertSame(
201+
'42.42',
202+
$this->toolBox->execute(new ToolCall('call_1234', 'tool_returning_float'))
203+
);
204+
}
205+
206+
#[Test]
207+
public function executeWithToolReturningStringable(): void
208+
{
209+
self::assertSame(
210+
'Hi!',
211+
$this->toolBox->execute(new ToolCall('call_1234', 'tool_returning_stringable'))
212+
);
121213
}
122214
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Tests\Fixture\Tool;
6+
7+
use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool;
8+
9+
#[AsTool('tool_returning_array', 'A tool returning an array')]
10+
final class ToolReturningArray
11+
{
12+
public function __invoke(): array
13+
{
14+
return ['foo' => 'bar'];
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Tests\Fixture\Tool;
6+
7+
use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool;
8+
9+
#[AsTool('tool_returning_float', 'A tool returning a float')]
10+
final class ToolReturningFloat
11+
{
12+
public function __invoke(): float
13+
{
14+
return 42.42;
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Tests\Fixture\Tool;
6+
7+
use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool;
8+
9+
#[AsTool('tool_returning_integer', 'A tool returning an integer')]
10+
final class ToolReturningInteger
11+
{
12+
public function __invoke(): int
13+
{
14+
return 42;
15+
}
16+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Tests\Fixture\Tool;
6+
7+
use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool;
8+
9+
#[AsTool('tool_returning_json_serializable', 'A tool returning an object which implements \JsonSerializable')]
10+
final class ToolReturningJsonSerializable
11+
{
12+
public function __invoke(): \JsonSerializable
13+
{
14+
return new class implements \JsonSerializable {
15+
public function jsonSerialize(): array
16+
{
17+
return ['foo' => 'bar'];
18+
}
19+
};
20+
}
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Tests\Fixture\Tool;
6+
7+
use PhpLlm\LlmChain\Chain\ToolBox\Attribute\AsTool;
8+
9+
#[AsTool('tool_returning_stringable', 'A tool returning an object which implements \Stringable')]
10+
final class ToolReturningStringable
11+
{
12+
public function __invoke(): \Stringable
13+
{
14+
return new class implements \Stringable {
15+
public function __toString(): string
16+
{
17+
return 'Hi!';
18+
}
19+
};
20+
}
21+
}

0 commit comments

Comments
 (0)