Skip to content

Commit ea023f8

Browse files
authored
feat: adding simple weather tool (#7)
1 parent 1233905 commit ea023f8

File tree

8 files changed

+99
-33
lines changed

8 files changed

+99
-33
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Provided Tools
5353
* [x] SerpApi
5454
* [x] Clock
5555
* [x] Wikipedia
56-
* [ ] Weather
56+
* [x] Weather
5757

5858
Usage Examples
5959
--------------

examples/toolchain-weather.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
use PhpLlm\LlmChain\Chain;
4+
use PhpLlm\LlmChain\Message\Message;
5+
use PhpLlm\LlmChain\Message\MessageBag;
6+
use PhpLlm\LlmChain\OpenAI\Model\Gpt;
7+
use PhpLlm\LlmChain\OpenAI\Model\Gpt\Version;
8+
use PhpLlm\LlmChain\OpenAI\Runtime\OpenAI;
9+
use PhpLlm\LlmChain\ToolBox\ParameterAnalyzer;
10+
use PhpLlm\LlmChain\ToolBox\Registry;
11+
use PhpLlm\LlmChain\ToolBox\Tool\OpenMeteo;
12+
use PhpLlm\LlmChain\ToolBox\ToolAnalyzer;
13+
use Symfony\Component\HttpClient\HttpClient;
14+
15+
require_once dirname(__DIR__).'/vendor/autoload.php';
16+
17+
$httpClient = HttpClient::create();
18+
$runtime = new OpenAI($httpClient, getenv('OPENAI_API_KEY'));
19+
$llm = new Gpt($runtime, Version::GPT_4o_MINI);
20+
21+
$wikipedia = new OpenMeteo($httpClient);
22+
$registry = new Registry(new ToolAnalyzer(new ParameterAnalyzer()), [$wikipedia]);
23+
$chain = new Chain($llm, $registry);
24+
25+
$messages = new MessageBag(Message::ofUser('How is the weather currently in Berlin?'));
26+
$response = $chain->call($messages);
27+
28+
echo $response.PHP_EOL;

src/ToolBox/Metadata.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
/**
88
* @phpstan-import-type ParameterDefinition from ParameterAnalyzer
99
*/
10-
final class Metadata
10+
final class Metadata implements \JsonSerializable
1111
{
1212
/**
1313
* @param ParameterDefinition|null $parameters
@@ -20,4 +20,31 @@ public function __construct(
2020
public readonly ?array $parameters,
2121
) {
2222
}
23+
24+
/**
25+
* @return array{
26+
* type: 'function',
27+
* function: array{
28+
* name: string,
29+
* description: string,
30+
* parameters?: ParameterDefinition
31+
* }
32+
* }
33+
*/
34+
public function jsonSerialize(): array
35+
{
36+
$function = [
37+
'name' => $this->name,
38+
'description' => $this->description,
39+
];
40+
41+
if (isset($this->parameters)) {
42+
$function['parameters'] = $this->parameters;
43+
}
44+
45+
return [
46+
'type' => 'function',
47+
'function' => $function,
48+
];
49+
}
2350
}

src/ToolBox/ParameterAnalyzer.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ public function getDefinition(string $className, string $methodName): ?array
4040
$paramType = 'integer';
4141
}
4242

43+
if ('float' === $paramType) {
44+
$paramType = 'number';
45+
}
46+
4347
if (!$parameter->isOptional()) {
4448
$result['required'][] = $paramName;
4549
}

src/ToolBox/Registry.php

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66

77
use PhpLlm\LlmChain\Response\ToolCall;
88

9-
/**
10-
* @phpstan-import-type ToolDefinition from RegistryInterface
11-
*/
129
final class Registry implements RegistryInterface
1310
{
1411
/**
@@ -17,7 +14,7 @@ final class Registry implements RegistryInterface
1714
private readonly array $tools;
1815

1916
/**
20-
* @var list<ToolDefinition>
17+
* @var Metadata[]
2118
*/
2219
private array $map;
2320

@@ -40,19 +37,7 @@ public function getMap(): array
4037
$map = [];
4138
foreach ($this->tools as $tool) {
4239
foreach ($this->toolAnalyzer->getMetadata($tool::class) as $metadata) {
43-
$function = [
44-
'name' => $metadata->name,
45-
'description' => $metadata->description,
46-
];
47-
48-
if (isset($metadata->parameters)) {
49-
$function['parameters'] = $metadata->parameters;
50-
}
51-
52-
$map[] = [
53-
'type' => 'function',
54-
'function' => $function,
55-
];
40+
$map[] = $metadata;
5641
}
5742
}
5843

src/ToolBox/RegistryInterface.php

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,10 @@
66

77
use PhpLlm\LlmChain\Response\ToolCall;
88

9-
/**
10-
* @phpstan-import-type ParameterDefinition from ParameterAnalyzer
11-
*
12-
* @phpstan-type ToolDefinition = array{
13-
* type: 'function',
14-
* function: array{
15-
* name: string,
16-
* description: string,
17-
* parameters?: ParameterDefinition
18-
* }
19-
* }
20-
*/
219
interface RegistryInterface
2210
{
2311
/**
24-
* @return list<ToolDefinition>
12+
* @return Metadata[]
2513
*/
2614
public function getMap(): array;
2715

src/ToolBox/Tool/OpenMeteo.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\ToolBox\Tool;
6+
7+
use PhpLlm\LlmChain\ToolBox\AsTool;
8+
use Symfony\Contracts\HttpClient\HttpClientInterface;
9+
10+
#[AsTool(name: 'weather', description: 'get the current weather for a location')]
11+
final readonly class OpenMeteo
12+
{
13+
public function __construct(
14+
private HttpClientInterface $httpClient,
15+
) {
16+
}
17+
18+
/**
19+
* @param float $latitude the latitude of the location
20+
* @param float $longitude the longitude of the location
21+
*/
22+
public function __invoke(float $latitude, float $longitude): string
23+
{
24+
$response = $this->httpClient->request('GET', 'https://api.open-meteo.com/v1/forecast', [
25+
'query' => [
26+
'latitude' => $latitude,
27+
'longitude' => $longitude,
28+
'current' => 'temperature_2m,wind_speed_10m',
29+
],
30+
]);
31+
32+
return $response->getContent();
33+
}
34+
}

tests/ToolBox/RegistryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public function testFunctionsMap(): void
8989
],
9090
];
9191

92-
self::assertSame($expected, $actual);
92+
self::assertSame(json_encode($expected), json_encode($actual));
9393
}
9494

9595
public function testExecute(): void

0 commit comments

Comments
 (0)