Skip to content

Commit

Permalink
feat: adding simple weather tool
Browse files Browse the repository at this point in the history
  • Loading branch information
chr-hertel committed Sep 21, 2024
1 parent 3513643 commit 33e61af
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 33 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Provided Tools
* [x] SerpApi
* [x] Clock
* [x] Wikipedia
* [ ] Weather
* [x] Weather

Usage Examples
--------------
Expand Down
28 changes: 28 additions & 0 deletions examples/toolchain-weather.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

use PhpLlm\LlmChain\Chain;
use PhpLlm\LlmChain\Message\Message;
use PhpLlm\LlmChain\Message\MessageBag;
use PhpLlm\LlmChain\OpenAI\Model\Gpt;
use PhpLlm\LlmChain\OpenAI\Model\Gpt\Version;
use PhpLlm\LlmChain\OpenAI\Runtime\OpenAI;
use PhpLlm\LlmChain\ToolBox\ParameterAnalyzer;
use PhpLlm\LlmChain\ToolBox\Registry;
use PhpLlm\LlmChain\ToolBox\Tool\OpenMeteo;
use PhpLlm\LlmChain\ToolBox\ToolAnalyzer;
use Symfony\Component\HttpClient\HttpClient;

require_once dirname(__DIR__).'/vendor/autoload.php';

$httpClient = HttpClient::create();
$runtime = new OpenAI($httpClient, getenv('OPENAI_API_KEY'));
$llm = new Gpt($runtime, Version::GPT_4o_MINI);

$wikipedia = new OpenMeteo($httpClient);
$registry = new Registry(new ToolAnalyzer(new ParameterAnalyzer()), [$wikipedia]);
$chain = new Chain($llm, $registry);

$messages = new MessageBag(Message::ofUser('How is the weather currently in Berlin?'));
$response = $chain->call($messages);

echo $response.PHP_EOL;
29 changes: 28 additions & 1 deletion src/ToolBox/Metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/**
* @phpstan-import-type ParameterDefinition from ParameterAnalyzer
*/
final class Metadata
final class Metadata implements \JsonSerializable
{
/**
* @param ParameterDefinition|null $parameters
Expand All @@ -20,4 +20,31 @@ public function __construct(
public readonly ?array $parameters,
) {
}

/**
* @return array{
* type: 'function',
* function: array{
* name: string,
* description: string,
* parameters?: ParameterDefinition
* }
* }
*/
public function jsonSerialize(): array
{
$function = [
'name' => $this->name,
'description' => $this->description,
];

if (isset($this->parameters)) {
$function['parameters'] = $this->parameters;
}

return [
'type' => 'function',
'function' => $function,
];
}
}
4 changes: 4 additions & 0 deletions src/ToolBox/ParameterAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ public function getDefinition(string $className, string $methodName): ?array
$paramType = 'integer';
}

if ('float' === $paramType) {
$paramType = 'number';
}

if (!$parameter->isOptional()) {
$result['required'][] = $paramName;
}
Expand Down
19 changes: 2 additions & 17 deletions src/ToolBox/Registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@

use PhpLlm\LlmChain\Response\ToolCall;

/**
* @phpstan-import-type ToolDefinition from RegistryInterface
*/
final class Registry implements RegistryInterface
{
/**
Expand All @@ -17,7 +14,7 @@ final class Registry implements RegistryInterface
private readonly array $tools;

/**
* @var list<ToolDefinition>
* @var Metadata[]
*/
private array $map;

Expand All @@ -40,19 +37,7 @@ public function getMap(): array
$map = [];
foreach ($this->tools as $tool) {
foreach ($this->toolAnalyzer->getMetadata($tool::class) as $metadata) {
$function = [
'name' => $metadata->name,
'description' => $metadata->description,
];

if (isset($metadata->parameters)) {
$function['parameters'] = $metadata->parameters;
}

$map[] = [
'type' => 'function',
'function' => $function,
];
$map[] = $metadata;
}
}

Expand Down
14 changes: 1 addition & 13 deletions src/ToolBox/RegistryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,10 @@

use PhpLlm\LlmChain\Response\ToolCall;

/**
* @phpstan-import-type ParameterDefinition from ParameterAnalyzer
*
* @phpstan-type ToolDefinition = array{
* type: 'function',
* function: array{
* name: string,
* description: string,
* parameters?: ParameterDefinition
* }
* }
*/
interface RegistryInterface
{
/**
* @return list<ToolDefinition>
* @return Metadata[]
*/
public function getMap(): array;

Expand Down
34 changes: 34 additions & 0 deletions src/ToolBox/Tool/OpenMeteo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\ToolBox\Tool;

use PhpLlm\LlmChain\ToolBox\AsTool;
use Symfony\Contracts\HttpClient\HttpClientInterface;

#[AsTool(name: 'weather', description: 'get the current weather for a location')]
final readonly class OpenMeteo
{
public function __construct(
private HttpClientInterface $httpClient,
) {
}

/**
* @param float $latitude the latitude of the location
* @param float $longitude the longitude of the location
*/
public function __invoke(float $latitude, float $longitude): string
{
$response = $this->httpClient->request('GET', 'https://api.open-meteo.com/v1/forecast', [
'query' => [
'latitude' => $latitude,
'longitude' => $longitude,
'current' => 'temperature_2m,wind_speed_10m',
],
]);

return $response->getContent();
}
}
2 changes: 1 addition & 1 deletion tests/ToolBox/RegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public function testFunctionsMap(): void
],
];

self::assertSame($expected, $actual);
self::assertSame(json_encode($expected), json_encode($actual));
}

public function testExecute(): void
Expand Down

0 comments on commit 33e61af

Please sign in to comment.