Skip to content

Commit a8639ac

Browse files
echo-yiyiyidandansamaxlightaime
authored
Update weather function API by using pyowm (camel-ai#334)
Co-authored-by: Tianqi Xu <[email protected]> Co-authored-by: lig <[email protected]>
1 parent b2338e7 commit a8639ac

File tree

9 files changed

+403
-35
lines changed

9 files changed

+403
-35
lines changed

.github/workflows/pytest_package.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ jobs:
2727
OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}"
2828
GOOGLE_API_KEY: "${{ secrets.GOOGLE_API_KEY }}"
2929
SEARCH_ENGINE_ID: "${{ secrets.SEARCH_ENGINE_ID }}"
30+
OPENWEATHERMAP_API_KEY: "${{ secrets.OPENWEATHERMAP_API_KEY }}"
3031
run: poetry run pytest --fast-test-mode test/
3132

3233
pytest_package_llm_test:
@@ -42,6 +43,7 @@ jobs:
4243
OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}"
4344
GOOGLE_API_KEY: "${{ secrets.GOOGLE_API_KEY }}"
4445
SEARCH_ENGINE_ID: "${{ secrets.SEARCH_ENGINE_ID }}"
46+
OPENWEATHERMAP_API_KEY: "${{ secrets.OPENWEATHERMAP_API_KEY }}"
4547
run: poetry run pytest --llm-test-only test/
4648

4749
pytest_package_very_slow_test:
@@ -57,4 +59,5 @@ jobs:
5759
OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}"
5860
GOOGLE_API_KEY: "${{ secrets.GOOGLE_API_KEY }}"
5961
SEARCH_ENGINE_ID: "${{ secrets.SEARCH_ENGINE_ID }}"
62+
OPENWEATHERMAP_API_KEY: "${{ secrets.OPENWEATHERMAP_API_KEY }}"
6063
run: poetry run pytest --very-slow-test-only test/

camel/functions/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
# limitations under the License.
1313
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
1414

15-
from .openai_function import OpenAIFunction
1615
from .math_functions import MATH_FUNCS
16+
from .openai_function import OpenAIFunction
1717
from .search_functions import SEARCH_FUNCS
18+
from .weather_functions import WEATHER_FUNCS
1819

1920
__all__ = [
2021
'OpenAIFunction',
2122
'MATH_FUNCS',
2223
'SEARCH_FUNCS',
24+
'WEATHER_FUNCS',
2325
]

camel/functions/data_io_functions.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@
1919
from io import BytesIO
2020
from typing import Any, Dict, List, Optional
2121

22-
import docx2txt
23-
import fitz
24-
from bs4 import BeautifulSoup
25-
2622

2723
class File(ABC):
2824
r"""Represents an uploaded file comprised of Documents"""
@@ -107,6 +103,12 @@ def from_bytes(cls, file: BytesIO) -> "DocxFile":
107103
DocxFile: A DocxFile object.
108104
"""
109105
# Use docx2txt to extract text from docx files
106+
try:
107+
import docx2txt
108+
except ImportError:
109+
raise ImportError("Please install `docx2txt` first. "
110+
"You can install it by running "
111+
"`pip install docx2txt`.")
110112
text = docx2txt.process(file)
111113
text = strip_consecutive_newlines(text)
112114
# Create a dictionary with the extracted text
@@ -132,6 +134,12 @@ def from_bytes(cls, file: BytesIO) -> "PdfFile":
132134
PdfFile: A PdfFile object.
133135
"""
134136
# Use fitz to extract text from pdf files
137+
try:
138+
import fitz
139+
except ImportError:
140+
raise ImportError("Please install `PyMuPDF` first. "
141+
"You can install it by running "
142+
"`pip install PyMuPDF`.")
135143
pdf = fitz.open(stream=file.read(), filetype="pdf")
136144
docs = []
137145
for i, page in enumerate(pdf):
@@ -210,6 +218,12 @@ def from_bytes(cls, file: BytesIO) -> "HtmlFile":
210218
HtmlFile: A HtmlFile object.
211219
"""
212220
# Parse the HTML data from the file
221+
try:
222+
from bs4 import BeautifulSoup
223+
except ImportError:
224+
raise ImportError("Please install `beautifulsoup4` first. "
225+
"You can install it by running "
226+
"`pip install beautifulsoup4`.")
213227
soup = BeautifulSoup(file, "html.parser")
214228
text = soup.get_text()
215229
text = strip_consecutive_newlines(text)

camel/functions/search_functions.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ def search_wiki(entity: str) -> str:
3131
string: The search result. If the page corresponding to the entity
3232
exists, return the summary of this entity in a string.
3333
"""
34-
import wikipedia
34+
try:
35+
import wikipedia
36+
except ImportError:
37+
raise ImportError(
38+
"Please install `wikipedia` first. You can install it by running "
39+
"`pip install wikipedia`.")
40+
3541
result: str
3642

3743
try:
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
2+
# Licensed under the Apache License, Version 2.0 (the “License”);
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an “AS IS” BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14+
import os
15+
from typing import List
16+
17+
from camel.functions import OpenAIFunction
18+
19+
20+
def get_openweathermap_api_key() -> str:
21+
r"""Retrieve the OpenWeatherMap API key from environment variables.
22+
23+
Returns:
24+
str: The OpenWeatherMap API key.
25+
26+
Raises:
27+
ValueError: If the API key is not found in the environment variables.
28+
"""
29+
# Get `OPENWEATHERMAP_API_KEY` here: https://openweathermap.org
30+
OPENWEATHERMAP_API_KEY = os.environ.get('OPENWEATHERMAP_API_KEY')
31+
if not OPENWEATHERMAP_API_KEY:
32+
raise ValueError("`OPENWEATHERMAP_API_KEY` not found in environment "
33+
"variables. Get `OPENWEATHERMAP_API_KEY` here: "
34+
"`https://openweathermap.org`.")
35+
return OPENWEATHERMAP_API_KEY
36+
37+
38+
def get_weather_data(city: str, temp_units: str = 'kelvin',
39+
wind_units: str = 'meters_sec',
40+
visibility_units: str = 'meters',
41+
time_units: str = 'unix') -> str:
42+
r"""Fetch and return a comprehensive weather report for a given city as a
43+
string. The report includes current weather conditions, temperature,
44+
wind details, visibility, and sunrise/sunset times, all formatted as
45+
46+
The function interacts with the OpenWeatherMap API to retrieve the data.
47+
48+
Args:
49+
city (string): The name of the city for which the weather information
50+
is desired. Format "City, CountryCode" (e.g., "Paris, FR"
51+
for Paris, France). If the country code is not provided,
52+
the API will search for the city in all countries, which
53+
may yield incorrect results if multiple cities with the
54+
same name exist.
55+
temp_units (string): Units for temperature. Options: 'kelvin',
56+
'celsius', 'fahrenheit'. (default: :obj:`kelvin`)
57+
wind_units (string): Units for wind speed. Options: 'meters_sec',
58+
'miles_hour', 'knots', 'beaufort'. (default: :obj:`meters_sec`)
59+
visibility_units (string): Units for visibility distance. Options:
60+
'meters', 'miles'. (default: :obj:`meters`)
61+
time_units (string): Format for sunrise and sunset times. Options:
62+
'unix', 'iso', 'date'. (default: :obj:`unix`)
63+
64+
Returns:
65+
str: A string containing the fetched weather data, formatted in a
66+
readable manner. If an error occurs, a message indicating the
67+
error will be returned instead.
68+
69+
Example of return string:
70+
"Weather in Paris, FR: 15°C, feels like 13°C. Max temp: 17°C, Min temp
71+
: 12°C.
72+
Wind: 5 m/s at 270 degrees. Visibility: 10 kilometers.
73+
Sunrise at 05:46:05 (UTC), Sunset at 18:42:20 (UTC)."
74+
75+
Note:
76+
Please ensure that the API key is valid and has permissions to access
77+
the weather data.
78+
"""
79+
# NOTE: This tool may not work as expected since the input arguments like
80+
# `time_units` should be enum types which are not supported yet.
81+
82+
try:
83+
import pyowm
84+
except ImportError:
85+
raise ImportError(
86+
"Please install `pyowm` first. You can install it by running "
87+
"`pip install pyowm`.")
88+
89+
OPENWEATHERMAP_API_KEY = get_openweathermap_api_key()
90+
owm = pyowm.OWM(OPENWEATHERMAP_API_KEY)
91+
mgr = owm.weather_manager()
92+
93+
try:
94+
observation = mgr.weather_at_place(city)
95+
weather = observation.weather
96+
97+
# Temperature
98+
temperature = weather.temperature(temp_units)
99+
100+
# Wind
101+
wind_data = observation.weather.wind(unit=wind_units)
102+
wind_speed = wind_data.get('speed')
103+
# 'N/A' if the degree is not available
104+
wind_deg = wind_data.get('deg', 'N/A')
105+
106+
# Visibility
107+
visibility_distance = observation.weather.visibility_distance
108+
visibility = (str(visibility_distance) if visibility_units == 'meters'
109+
else str(observation.weather.visibility(unit='miles')))
110+
111+
# Sunrise and Sunset
112+
sunrise_time = str(weather.sunrise_time(timeformat=time_units))
113+
sunset_time = str(weather.sunset_time(timeformat=time_units))
114+
115+
# Compile all the weather details into a report string
116+
weather_report = (
117+
f"Weather in {city}: {temperature['temp']}°{temp_units.title()}, "
118+
f"feels like {temperature['feels_like']}°{temp_units.title()}. "
119+
f"Max temp: {temperature['temp_max']}°{temp_units.title()}, "
120+
f"Min temp: {temperature['temp_min']}°{temp_units.title()}. "
121+
f"Wind: {wind_speed} {wind_units} at {wind_deg} degrees. "
122+
f"Visibility: {visibility} {visibility_units}. "
123+
f"Sunrise at {sunrise_time}, Sunset at {sunset_time}.")
124+
125+
return weather_report
126+
127+
except Exception as e:
128+
error_message = (
129+
f"An error occurred while fetching weather data for {city}: "
130+
f"{str(e)}.")
131+
return error_message
132+
133+
134+
WEATHER_FUNCS: List[OpenAIFunction] = [
135+
OpenAIFunction(func) for func in [get_weather_data]
136+
]

examples/function_call/role_playing_with_function.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,21 @@
1717

1818
from camel.agents.chat_agent import FunctionCallingRecord
1919
from camel.configs import ChatGPTConfig, FunctionCallingConfig
20-
from camel.functions import MATH_FUNCS, SEARCH_FUNCS
20+
from camel.functions import MATH_FUNCS, SEARCH_FUNCS, WEATHER_FUNCS
2121
from camel.societies import RolePlaying
2222
from camel.types import ModelType
2323
from camel.utils import print_text_animated
2424

2525

2626
def main(model_type=ModelType.GPT_4) -> None:
2727
task_prompt = ("Assuming the current year is 2023, estimate KAUST's "
28-
"current age and then add 10 more years to this age.")
28+
"current age and then add 10 more years to this age, "
29+
"and get the current weather of the city where KAUST "
30+
"is located.")
2931

3032
user_model_config = ChatGPTConfig(temperature=0.0)
3133

32-
function_list = [*MATH_FUNCS, *SEARCH_FUNCS]
34+
function_list = [*MATH_FUNCS, *SEARCH_FUNCS, *WEATHER_FUNCS]
3335
assistant_model_config = FunctionCallingConfig.from_openai_function_list(
3436
function_list=function_list,
3537
kwargs=dict(temperature=0.0),

0 commit comments

Comments
 (0)