Skip to content

Commit 6823083

Browse files
committed
Add symfony/ai-openmeteo-tool bridge
Extract the OpenMeteo tool from the main toolbox into a standalone bridge package, following the pattern established by Brave and Tavily bridges. This extraction creates a complete standalone package structure with: - New Bridge/OpenMeteo namespace - Dedicated composer package (symfony/ai-openmeteo-tool) - Comprehensive test suite with fixtures - Standard bridge infrastructure (LICENSE, CHANGELOG, phpunit.xml.dist) - Updated documentation and examples
1 parent 1f2a8f1 commit 6823083

File tree

15 files changed

+392
-3
lines changed

15 files changed

+392
-3
lines changed

demo/config/packages/ai.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ services:
106106
autoconfigure: true
107107

108108
# Symfony\AI\Agent\Toolbox\Tool\Clock: ~
109-
# Symfony\AI\Agent\Toolbox\Tool\OpenMeteo: ~
109+
# Symfony\AI\Agent\Bridge\OpenMeteo\OpenMeteo: ~
110110
# Symfony\AI\Agent\Toolbox\Tool\SerpApi:
111111
# $apiKey: '%env(SERP_API_KEY)%'
112112
Symfony\AI\Agent\Toolbox\Tool\Wikipedia: ~

docs/bundles/ai-bundle.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,7 @@ To use existing tools, you can register them as a service:
781781
autoconfigure: true
782782
783783
Symfony\AI\Agent\Toolbox\Tool\Clock: ~
784-
Symfony\AI\Agent\Toolbox\Tool\OpenMeteo: ~
784+
Symfony\AI\Agent\Bridge\OpenMeteo\OpenMeteo: ~
785785
Symfony\AI\Agent\Toolbox\Tool\SerpApi:
786786
$apiKey: '%env(SERP_API_KEY)%'
787787
Symfony\AI\Agent\Toolbox\Tool\SimilaritySearch: ~

examples/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"symfony/ai-agent": "@dev",
2121
"symfony/ai-brave-tool": "@dev",
2222
"symfony/ai-chat": "@dev",
23+
"symfony/ai-openmeteo-tool": "@dev",
2324
"symfony/ai-platform": "@dev",
2425
"symfony/ai-store": "@dev",
2526
"symfony/ai-tavily-tool": "@dev",

examples/toolbox/weather-event.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
use Symfony\AI\Agent\Agent;
1313
use Symfony\AI\Agent\Toolbox\AgentProcessor;
1414
use Symfony\AI\Agent\Toolbox\Event\ToolCallsExecuted;
15-
use Symfony\AI\Agent\Toolbox\Tool\OpenMeteo;
15+
use Symfony\AI\Agent\Bridge\OpenMeteo\OpenMeteo;
1616
use Symfony\AI\Agent\Toolbox\Toolbox;
1717
use Symfony\AI\Platform\Bridge\OpenAi\PlatformFactory;
1818
use Symfony\AI\Platform\Message\Message;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/Tests export-ignore
2+
/phpunit.xml.dist export-ignore
3+
/.git* export-ignore
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Close Pull Request
2+
3+
on:
4+
pull_request_target:
5+
types: [opened]
6+
7+
jobs:
8+
run:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: superbrothers/close-pull-request@v3
12+
with:
13+
comment: |
14+
Thanks for your Pull Request! We love contributions.
15+
16+
However, you should instead open your PR on the main repository:
17+
https://github.com/symfony/ai
18+
19+
This repository is what we call a "subtree split": a read-only subset of that main repository.
20+
We're looking forward to your PR there!
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml
4+
.phpunit.result.cache
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHANGELOG
2+
=========
3+
4+
0.1
5+
---
6+
7+
* Add the bridge
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2025-present Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\AI\Agent\Bridge\OpenMeteo;
13+
14+
use Symfony\AI\Agent\Toolbox\Attribute\AsTool;
15+
use Symfony\AI\Platform\Contract\JsonSchema\Attribute\With;
16+
use Symfony\Contracts\HttpClient\HttpClientInterface;
17+
18+
/**
19+
* @author Christopher Hertel <[email protected]>
20+
*/
21+
#[AsTool(name: 'weather_current', description: 'get current weather for a location', method: 'current')]
22+
#[AsTool(name: 'weather_forecast', description: 'get weather forecast for a location', method: 'forecast')]
23+
final class OpenMeteo
24+
{
25+
private const WMO_CODES = [
26+
0 => 'Clear',
27+
1 => 'Mostly Clear',
28+
2 => 'Partly Cloudy',
29+
3 => 'Overcast',
30+
45 => 'Fog',
31+
48 => 'Icy Fog',
32+
51 => 'Light Drizzle',
33+
53 => 'Drizzle',
34+
55 => 'Heavy Drizzle',
35+
56 => 'Light Freezing Drizzle',
36+
57 => 'Freezing Drizzle',
37+
61 => 'Light Rain',
38+
63 => 'Rain',
39+
65 => 'Heavy Rain',
40+
66 => 'Light Freezing Rain',
41+
67 => 'Freezing Rain',
42+
71 => 'Light Snow',
43+
73 => 'Snow',
44+
75 => 'Heavy Snow',
45+
77 => 'Snow Grains',
46+
80 => 'Light Showers',
47+
81 => 'Showers',
48+
82 => 'Heavy Showers',
49+
85 => 'Light Snow Showers',
50+
86 => 'Snow Showers',
51+
95 => 'Thunderstorm',
52+
96 => 'Light Thunderstorm with Hail',
53+
99 => 'Thunderstorm with Hail',
54+
];
55+
56+
public function __construct(
57+
private readonly HttpClientInterface $httpClient,
58+
) {
59+
}
60+
61+
/**
62+
* @param float $latitude the latitude of the location
63+
* @param float $longitude the longitude of the location
64+
*
65+
* @return array{
66+
* weather: string,
67+
* time: string,
68+
* temperature: string,
69+
* wind_speed: string,
70+
* }
71+
*/
72+
public function current(float $latitude, float $longitude): array
73+
{
74+
$result = $this->httpClient->request('GET', 'https://api.open-meteo.com/v1/forecast', [
75+
'query' => [
76+
'latitude' => $latitude,
77+
'longitude' => $longitude,
78+
'current' => 'weather_code,temperature_2m,wind_speed_10m',
79+
],
80+
]);
81+
82+
$data = $result->toArray();
83+
84+
return [
85+
'weather' => self::WMO_CODES[$data['current']['weather_code']] ?? 'Unknown',
86+
'time' => $data['current']['time'],
87+
'temperature' => $data['current']['temperature_2m'].$data['current_units']['temperature_2m'],
88+
'wind_speed' => $data['current']['wind_speed_10m'].$data['current_units']['wind_speed_10m'],
89+
];
90+
}
91+
92+
/**
93+
* @param float $latitude the latitude of the location
94+
* @param float $longitude the longitude of the location
95+
* @param int $days the number of days to forecast
96+
*
97+
* @return array{
98+
* weather: string,
99+
* time: string,
100+
* temperature_min: string,
101+
* temperature_max: string,
102+
* }[]
103+
*/
104+
public function forecast(
105+
float $latitude,
106+
float $longitude,
107+
#[With(minimum: 1, maximum: 16)]
108+
int $days = 7,
109+
): array {
110+
$result = $this->httpClient->request('GET', 'https://api.open-meteo.com/v1/forecast', [
111+
'query' => [
112+
'latitude' => $latitude,
113+
'longitude' => $longitude,
114+
'daily' => 'weather_code,temperature_2m_max,temperature_2m_min',
115+
'forecast_days' => $days,
116+
],
117+
]);
118+
119+
$data = $result->toArray();
120+
$forecast = [];
121+
for ($i = 0; $i < $days; ++$i) {
122+
$forecast[] = [
123+
'weather' => self::WMO_CODES[$data['daily']['weather_code'][$i]] ?? 'Unknown',
124+
'time' => $data['daily']['time'][$i],
125+
'temperature_min' => $data['daily']['temperature_2m_min'][$i].$data['daily_units']['temperature_2m_min'],
126+
'temperature_max' => $data['daily']['temperature_2m_max'][$i].$data['daily_units']['temperature_2m_max'],
127+
];
128+
}
129+
130+
return $forecast;
131+
}
132+
}

0 commit comments

Comments
 (0)