camel-ai 0.1.5.6__py3-none-any.whl → 0.1.6.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

Files changed (133) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +249 -36
  3. camel/agents/critic_agent.py +18 -2
  4. camel/agents/deductive_reasoner_agent.py +16 -4
  5. camel/agents/embodied_agent.py +20 -6
  6. camel/agents/knowledge_graph_agent.py +24 -5
  7. camel/agents/role_assignment_agent.py +13 -1
  8. camel/agents/search_agent.py +16 -5
  9. camel/agents/task_agent.py +20 -5
  10. camel/configs/__init__.py +11 -9
  11. camel/configs/anthropic_config.py +5 -6
  12. camel/configs/base_config.py +50 -4
  13. camel/configs/gemini_config.py +69 -17
  14. camel/configs/groq_config.py +105 -0
  15. camel/configs/litellm_config.py +2 -8
  16. camel/configs/mistral_config.py +78 -0
  17. camel/configs/ollama_config.py +5 -7
  18. camel/configs/openai_config.py +12 -23
  19. camel/configs/vllm_config.py +102 -0
  20. camel/configs/zhipuai_config.py +5 -11
  21. camel/embeddings/__init__.py +2 -0
  22. camel/embeddings/mistral_embedding.py +89 -0
  23. camel/human.py +1 -1
  24. camel/interpreters/__init__.py +2 -0
  25. camel/interpreters/ipython_interpreter.py +167 -0
  26. camel/loaders/__init__.py +2 -0
  27. camel/loaders/firecrawl_reader.py +213 -0
  28. camel/memories/agent_memories.py +1 -4
  29. camel/memories/blocks/chat_history_block.py +6 -2
  30. camel/memories/blocks/vectordb_block.py +3 -1
  31. camel/memories/context_creators/score_based.py +6 -6
  32. camel/memories/records.py +9 -7
  33. camel/messages/base.py +1 -0
  34. camel/models/__init__.py +8 -0
  35. camel/models/anthropic_model.py +7 -2
  36. camel/models/azure_openai_model.py +152 -0
  37. camel/models/base_model.py +9 -2
  38. camel/models/gemini_model.py +14 -2
  39. camel/models/groq_model.py +131 -0
  40. camel/models/litellm_model.py +26 -4
  41. camel/models/mistral_model.py +169 -0
  42. camel/models/model_factory.py +30 -3
  43. camel/models/ollama_model.py +21 -2
  44. camel/models/open_source_model.py +13 -5
  45. camel/models/openai_model.py +7 -2
  46. camel/models/stub_model.py +4 -4
  47. camel/models/vllm_model.py +138 -0
  48. camel/models/zhipuai_model.py +7 -4
  49. camel/prompts/__init__.py +8 -1
  50. camel/prompts/image_craft.py +34 -0
  51. camel/prompts/multi_condition_image_craft.py +34 -0
  52. camel/prompts/task_prompt_template.py +10 -4
  53. camel/prompts/{descripte_video_prompt.py → video_description_prompt.py} +1 -1
  54. camel/responses/agent_responses.py +4 -3
  55. camel/retrievers/auto_retriever.py +2 -2
  56. camel/societies/babyagi_playing.py +6 -4
  57. camel/societies/role_playing.py +16 -8
  58. camel/storages/graph_storages/graph_element.py +10 -14
  59. camel/storages/graph_storages/neo4j_graph.py +5 -0
  60. camel/storages/vectordb_storages/base.py +24 -13
  61. camel/storages/vectordb_storages/milvus.py +1 -1
  62. camel/storages/vectordb_storages/qdrant.py +2 -3
  63. camel/tasks/__init__.py +22 -0
  64. camel/tasks/task.py +408 -0
  65. camel/tasks/task_prompt.py +65 -0
  66. camel/toolkits/__init__.py +39 -0
  67. camel/toolkits/base.py +4 -2
  68. camel/toolkits/code_execution.py +1 -1
  69. camel/toolkits/dalle_toolkit.py +146 -0
  70. camel/toolkits/github_toolkit.py +19 -34
  71. camel/toolkits/google_maps_toolkit.py +368 -0
  72. camel/toolkits/math_toolkit.py +79 -0
  73. camel/toolkits/open_api_toolkit.py +547 -0
  74. camel/{functions → toolkits}/openai_function.py +2 -7
  75. camel/toolkits/retrieval_toolkit.py +76 -0
  76. camel/toolkits/search_toolkit.py +326 -0
  77. camel/toolkits/slack_toolkit.py +308 -0
  78. camel/toolkits/twitter_toolkit.py +522 -0
  79. camel/toolkits/weather_toolkit.py +173 -0
  80. camel/types/enums.py +154 -35
  81. camel/utils/__init__.py +14 -2
  82. camel/utils/async_func.py +1 -1
  83. camel/utils/commons.py +152 -2
  84. camel/utils/constants.py +3 -0
  85. camel/utils/token_counting.py +148 -40
  86. camel/workforce/__init__.py +23 -0
  87. camel/workforce/base.py +50 -0
  88. camel/workforce/manager_node.py +299 -0
  89. camel/workforce/role_playing_node.py +168 -0
  90. camel/workforce/single_agent_node.py +77 -0
  91. camel/workforce/task_channel.py +173 -0
  92. camel/workforce/utils.py +97 -0
  93. camel/workforce/worker_node.py +115 -0
  94. camel/workforce/workforce.py +49 -0
  95. camel/workforce/workforce_prompt.py +125 -0
  96. {camel_ai-0.1.5.6.dist-info → camel_ai-0.1.6.1.dist-info}/METADATA +45 -3
  97. camel_ai-0.1.6.1.dist-info/RECORD +182 -0
  98. camel/functions/__init__.py +0 -51
  99. camel/functions/google_maps_function.py +0 -335
  100. camel/functions/math_functions.py +0 -61
  101. camel/functions/open_api_function.py +0 -508
  102. camel/functions/retrieval_functions.py +0 -61
  103. camel/functions/search_functions.py +0 -298
  104. camel/functions/slack_functions.py +0 -286
  105. camel/functions/twitter_function.py +0 -479
  106. camel/functions/weather_functions.py +0 -144
  107. camel_ai-0.1.5.6.dist-info/RECORD +0 -157
  108. /camel/{functions → toolkits}/open_api_specs/biztoc/__init__.py +0 -0
  109. /camel/{functions → toolkits}/open_api_specs/biztoc/ai-plugin.json +0 -0
  110. /camel/{functions → toolkits}/open_api_specs/biztoc/openapi.yaml +0 -0
  111. /camel/{functions → toolkits}/open_api_specs/coursera/__init__.py +0 -0
  112. /camel/{functions → toolkits}/open_api_specs/coursera/openapi.yaml +0 -0
  113. /camel/{functions → toolkits}/open_api_specs/create_qr_code/__init__.py +0 -0
  114. /camel/{functions → toolkits}/open_api_specs/create_qr_code/openapi.yaml +0 -0
  115. /camel/{functions → toolkits}/open_api_specs/klarna/__init__.py +0 -0
  116. /camel/{functions → toolkits}/open_api_specs/klarna/openapi.yaml +0 -0
  117. /camel/{functions → toolkits}/open_api_specs/nasa_apod/__init__.py +0 -0
  118. /camel/{functions → toolkits}/open_api_specs/nasa_apod/openapi.yaml +0 -0
  119. /camel/{functions → toolkits}/open_api_specs/outschool/__init__.py +0 -0
  120. /camel/{functions → toolkits}/open_api_specs/outschool/ai-plugin.json +0 -0
  121. /camel/{functions → toolkits}/open_api_specs/outschool/openapi.yaml +0 -0
  122. /camel/{functions → toolkits}/open_api_specs/outschool/paths/__init__.py +0 -0
  123. /camel/{functions → toolkits}/open_api_specs/outschool/paths/get_classes.py +0 -0
  124. /camel/{functions → toolkits}/open_api_specs/outschool/paths/search_teachers.py +0 -0
  125. /camel/{functions → toolkits}/open_api_specs/security_config.py +0 -0
  126. /camel/{functions → toolkits}/open_api_specs/speak/__init__.py +0 -0
  127. /camel/{functions → toolkits}/open_api_specs/speak/openapi.yaml +0 -0
  128. /camel/{functions → toolkits}/open_api_specs/web_scraper/__init__.py +0 -0
  129. /camel/{functions → toolkits}/open_api_specs/web_scraper/ai-plugin.json +0 -0
  130. /camel/{functions → toolkits}/open_api_specs/web_scraper/openapi.yaml +0 -0
  131. /camel/{functions → toolkits}/open_api_specs/web_scraper/paths/__init__.py +0 -0
  132. /camel/{functions → toolkits}/open_api_specs/web_scraper/paths/scraper.py +0 -0
  133. {camel_ai-0.1.5.6.dist-info → camel_ai-0.1.6.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,146 @@
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 base64
15
+ import os
16
+ import uuid
17
+ from io import BytesIO
18
+ from typing import List, Optional
19
+
20
+ from openai import OpenAI
21
+ from PIL import Image
22
+
23
+ from camel.toolkits import OpenAIFunction
24
+ from camel.toolkits.base import BaseToolkit
25
+
26
+
27
+ class DalleToolkit(BaseToolkit):
28
+ r"""A class representing a toolkit for image generation using OpenAI's.
29
+
30
+ This class provides methods handle image generation using OpenAI's DALL-E.
31
+ """
32
+
33
+ def base64_to_image(self, base64_string: str) -> Optional[Image.Image]:
34
+ r"""Converts a base64 encoded string into a PIL Image object.
35
+
36
+ Args:
37
+ base64_string (str): The base64 encoded string of the image.
38
+
39
+ Returns:
40
+ Optional[Image.Image]: The PIL Image object or None if conversion
41
+ fails.
42
+ """
43
+ try:
44
+ # Decode the base64 string to get the image data
45
+ image_data = base64.b64decode(base64_string)
46
+ # Create a memory buffer for the image data
47
+ image_buffer = BytesIO(image_data)
48
+ # Open the image using the PIL library
49
+ image = Image.open(image_buffer)
50
+ return image
51
+ except Exception as e:
52
+ print(f"An error occurred while converting base64 to image: {e}")
53
+ return None
54
+
55
+ def image_path_to_base64(self, image_path: str) -> str:
56
+ r"""Converts the file path of an image to a Base64 encoded string.
57
+
58
+ Args:
59
+ image_path (str): The path to the image file.
60
+
61
+ Returns:
62
+ str: A Base64 encoded string representing the content of the image
63
+ file.
64
+ """
65
+ try:
66
+ with open(image_path, "rb") as image_file:
67
+ return base64.b64encode(image_file.read()).decode('utf-8')
68
+ except Exception as e:
69
+ print(
70
+ f"An error occurred while converting image path to base64: {e}"
71
+ )
72
+ return ""
73
+
74
+ def image_to_base64(self, image: Image.Image) -> str:
75
+ r"""Converts an image into a base64-encoded string.
76
+
77
+ This function takes an image object as input, encodes the image into a
78
+ PNG format base64 string, and returns it.
79
+ If the encoding process encounters an error, it prints the error
80
+ message and returns None.
81
+
82
+ Args:
83
+ image: The image object to be encoded, supports any image format
84
+ that can be saved in PNG format.
85
+
86
+ Returns:
87
+ str: A base64-encoded string of the image.
88
+ """
89
+ try:
90
+ with BytesIO() as buffered_image:
91
+ image.save(buffered_image, format="PNG")
92
+ buffered_image.seek(0)
93
+ image_bytes = buffered_image.read()
94
+ base64_str = base64.b64encode(image_bytes).decode('utf-8')
95
+ return base64_str
96
+ except Exception as e:
97
+ print(f"An error occurred: {e}")
98
+ return ""
99
+
100
+ def get_dalle_img(self, prompt: str, image_dir: str = "img") -> str:
101
+ r"""Generate an image using OpenAI's DALL-E model.
102
+ The generated image is saved to the specified directory.
103
+
104
+ Args:
105
+ prompt (str): The text prompt based on which the image is
106
+ generated.
107
+ image_dir (str): The directory to save the generated image.
108
+ Defaults to 'img'.
109
+
110
+ Returns:
111
+ str: The path to the saved image.
112
+ """
113
+
114
+ dalle_client = OpenAI()
115
+ response = dalle_client.images.generate(
116
+ model="dall-e-3",
117
+ prompt=prompt,
118
+ size="1024x1792",
119
+ quality="standard",
120
+ n=1, # NOTE: now dall-e-3 only supports n=1
121
+ response_format="b64_json",
122
+ )
123
+ image_b64 = response.data[0].b64_json
124
+ image = self.base64_to_image(image_b64) # type: ignore[arg-type]
125
+
126
+ if image is None:
127
+ raise ValueError("Failed to convert base64 string to image.")
128
+
129
+ os.makedirs(image_dir, exist_ok=True)
130
+ image_path = os.path.join(image_dir, f"{uuid.uuid4()}.png")
131
+ image.save(image_path)
132
+
133
+ return image_path
134
+
135
+ def get_tools(self) -> List[OpenAIFunction]:
136
+ r"""Returns a list of OpenAIFunction objects representing the
137
+ functions in the toolkit.
138
+
139
+ Returns:
140
+ List[OpenAIFunction]: A list of OpenAIFunction objects
141
+ representing the functions in the toolkit.
142
+ """
143
+ return [OpenAIFunction(self.get_dalle_img)]
144
+
145
+
146
+ DALLE_FUNCS: List[OpenAIFunction] = DalleToolkit().get_tools()
@@ -13,17 +13,18 @@
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
14
 
15
15
  import os
16
- from dataclasses import dataclass
17
16
  from datetime import datetime, timedelta
18
17
  from typing import List, Optional
19
18
 
20
- from camel.functions import OpenAIFunction
21
- from camel.toolkits.base import BaseToolkit
19
+ from pydantic import BaseModel
20
+
22
21
  from camel.utils import dependencies_required
23
22
 
23
+ from .base import BaseToolkit
24
+ from .openai_function import OpenAIFunction
25
+
24
26
 
25
- @dataclass
26
- class GithubIssue:
27
+ class GithubIssue(BaseModel):
27
28
  r"""Represents a GitHub issue.
28
29
 
29
30
  Attributes:
@@ -34,29 +35,11 @@ class GithubIssue:
34
35
  file_content (str): The content of the file associated with the issue.
35
36
  """
36
37
 
37
- def __init__(
38
- self,
39
- title: str,
40
- body: str,
41
- number: int,
42
- file_path: str,
43
- file_content: str,
44
- ) -> None:
45
- r"""Initialize a GithubIssue object.
46
-
47
- Args:
48
- title (str): The title of the GitHub issue.
49
- body (str): The body/content of the GitHub issue.
50
- number (int): The issue number.
51
- file_path (str): The path of the file associated with the issue.
52
- file_content (str): The content of the file associated with the
53
- issue.
54
- """
55
- self.title = title
56
- self.body = body
57
- self.number = number
58
- self.file_path = file_path
59
- self.file_content = file_content
38
+ title: str
39
+ body: str
40
+ number: int
41
+ file_path: str
42
+ file_content: str
60
43
 
61
44
  def __str__(self) -> str:
62
45
  r"""Returns a string representation of the issue.
@@ -74,8 +57,7 @@ class GithubIssue:
74
57
  )
75
58
 
76
59
 
77
- @dataclass
78
- class GithubPullRequestDiff:
60
+ class GithubPullRequestDiff(BaseModel):
79
61
  r"""Represents a single diff of a pull request on Github.
80
62
 
81
63
  Attributes:
@@ -91,8 +73,7 @@ class GithubPullRequestDiff:
91
73
  return f"Filename: {self.filename}\nPatch: {self.patch}"
92
74
 
93
75
 
94
- @dataclass
95
- class GithubPullRequest:
76
+ class GithubPullRequest(BaseModel):
96
77
  r"""Represents a pull request on Github.
97
78
 
98
79
  Attributes:
@@ -250,13 +231,17 @@ class GithubToolkit(BaseToolkit):
250
231
  and pr.merged_at is not None
251
232
  and pr.merged_at.timestamp() > earliest_date.timestamp()
252
233
  ):
253
- pr_details = GithubPullRequest(pr.title, pr.body, [])
234
+ pr_details = GithubPullRequest(
235
+ title=pr.title, body=pr.body, diffs=[]
236
+ )
254
237
 
255
238
  # Get files changed in the PR
256
239
  files = pr.get_files()
257
240
 
258
241
  for file in files:
259
- diff = GithubPullRequestDiff(file.filename, file.patch)
242
+ diff = GithubPullRequestDiff(
243
+ filename=file.filename, patch=file.patch
244
+ )
260
245
  pr_details.diffs.append(diff)
261
246
 
262
247
  merged_prs.append(str(pr_details))
@@ -0,0 +1,368 @@
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 functools import wraps
16
+ from typing import Any, Callable, List, Optional, Tuple, Union
17
+
18
+ from camel.toolkits.base import BaseToolkit
19
+ from camel.toolkits.openai_function import OpenAIFunction
20
+
21
+
22
+ def handle_googlemaps_exceptions(
23
+ func: Callable[..., Any],
24
+ ) -> Callable[..., Any]:
25
+ r"""Decorator to catch and handle exceptions raised by Google Maps API
26
+ calls.
27
+
28
+ Args:
29
+ func (Callable): The function to be wrapped by the decorator.
30
+
31
+ Returns:
32
+ Callable: A wrapper function that calls the wrapped function and
33
+ handles exceptions.
34
+ """
35
+
36
+ @wraps(func)
37
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
38
+ try:
39
+ # ruff: noqa: E501
40
+ from googlemaps.exceptions import ( # type: ignore[import]
41
+ ApiError,
42
+ HTTPError,
43
+ Timeout,
44
+ TransportError,
45
+ )
46
+ except ImportError:
47
+ raise ImportError(
48
+ "Please install `googlemaps` first. You can install "
49
+ "it by running `pip install googlemaps`."
50
+ )
51
+
52
+ try:
53
+ return func(*args, **kwargs)
54
+ except ApiError as e:
55
+ return (
56
+ 'An exception returned by the remote API. '
57
+ f'Status: {e.status}, Message: {e.message}'
58
+ )
59
+ except HTTPError as e:
60
+ return (
61
+ 'An unexpected HTTP error occurred. '
62
+ f'Status Code: {e.status_code}'
63
+ )
64
+ except Timeout:
65
+ return 'The request timed out.'
66
+ except TransportError as e:
67
+ return (
68
+ 'Something went wrong while trying to execute the '
69
+ f'request. Details: {e.base_exception}'
70
+ )
71
+ except Exception as e:
72
+ return f'An unexpected error occurred: {e}'
73
+
74
+ return wrapper
75
+
76
+
77
+ class GoogleMapsToolkit(BaseToolkit):
78
+ r"""A class representing a toolkit for interacting with GoogleMaps API.
79
+
80
+ This class provides methods for validating addresses, retrieving elevation,
81
+ and fetching timezone information using the Google Maps API.
82
+ """
83
+
84
+ def _import_googlemaps_or_raise(self) -> Any:
85
+ r"""Attempts to import the `googlemaps` library and returns it.
86
+
87
+ Returns:
88
+ module: The `googlemaps` module if successfully imported.
89
+
90
+ Raises:
91
+ ImportError: If the `googlemaps` library is not installed, this
92
+ error is raised with a message instructing how to install the
93
+ library using pip.
94
+ """
95
+ try:
96
+ import googlemaps
97
+
98
+ return googlemaps
99
+ except ImportError:
100
+ raise ImportError(
101
+ "Please install `googlemaps` first. You can install "
102
+ "it by running `pip install googlemaps`."
103
+ )
104
+
105
+ def _get_googlemaps_api_key(self) -> str:
106
+ r"""Retrieve the Google Maps API key from environment variables.
107
+
108
+ Returns:
109
+ str: The Google Maps API key.
110
+
111
+ Raises:
112
+ ValueError: If the API key is not found in the environment
113
+ variables.
114
+ """
115
+ # Get `GOOGLEMAPS_API_KEY` here:
116
+ # https://console.cloud.google.com/apis/credentials
117
+ GOOGLEMAPS_API_KEY = os.environ.get('GOOGLEMAPS_API_KEY')
118
+ if not GOOGLEMAPS_API_KEY:
119
+ raise ValueError(
120
+ "`GOOGLEMAPS_API_KEY` not found in environment "
121
+ "variables. `GOOGLEMAPS_API_KEY` API keys are "
122
+ "generated in the `Credentials` page of the "
123
+ "`APIs & Services` tab of "
124
+ "https://console.cloud.google.com/apis/credentials."
125
+ )
126
+ return GOOGLEMAPS_API_KEY
127
+
128
+ def get_address_description(
129
+ self,
130
+ address: Union[str, List[str]],
131
+ region_code: Optional[str] = None,
132
+ locality: Optional[str] = None,
133
+ ) -> str:
134
+ r"""Validates an address via Google Maps API, returns a descriptive
135
+ summary.
136
+
137
+ Validates an address using Google Maps API, returning a summary that
138
+ includes information on address completion, formatted address, location
139
+ coordinates, and metadata types that are true for the given address.
140
+
141
+ Args:
142
+ address (Union[str, List[str]]): The address or components to
143
+ validate. Can be a single string or a list representing
144
+ different parts.
145
+ region_code (str, optional): Country code for regional restriction,
146
+ helps narrowing down results. (default: :obj:`None`)
147
+ locality (str, optional): Restricts validation to a specific
148
+ locality, e.g., "Mountain View". (default: :obj:`None`)
149
+
150
+ Returns:
151
+ str: Summary of the address validation results, including
152
+ information on address completion, formatted address,
153
+ geographical coordinates (latitude and longitude), and metadata
154
+ types true for the address.
155
+
156
+ Raises:
157
+ ImportError: If the `googlemaps` library is not installed.
158
+ Exception: For unexpected errors during the address validation.
159
+ """
160
+ googlemaps = self._import_googlemaps_or_raise()
161
+ google_maps_api_key = self._get_googlemaps_api_key()
162
+ try:
163
+ gmaps = googlemaps.Client(key=google_maps_api_key)
164
+ except Exception as e:
165
+ return f"Error: {e!s}"
166
+
167
+ try:
168
+ addressvalidation_result = gmaps.addressvalidation(
169
+ [address],
170
+ regionCode=region_code,
171
+ locality=locality,
172
+ enableUspsCass=False,
173
+ ) # Always False as per requirements
174
+
175
+ # Check if the result contains an error
176
+ if 'error' in addressvalidation_result:
177
+ error_info = addressvalidation_result['error']
178
+ error_message = error_info.get(
179
+ 'message', 'An unknown error occurred'
180
+ )
181
+ error_status = error_info.get('status', 'UNKNOWN_STATUS')
182
+ error_code = error_info.get('code', 'UNKNOWN_CODE')
183
+ return (
184
+ f"Address validation failed with error: {error_message} "
185
+ f"Status: {error_status}, Code: {error_code}"
186
+ )
187
+
188
+ # Assuming the successful response structure
189
+ # includes a 'result' key
190
+ result = addressvalidation_result['result']
191
+ verdict = result.get('verdict', {})
192
+ address_info = result.get('address', {})
193
+ geocode = result.get('geocode', {})
194
+ metadata = result.get('metadata', {})
195
+
196
+ # Construct the descriptive string
197
+ address_complete = (
198
+ "Yes" if verdict.get('addressComplete', False) else "No"
199
+ )
200
+ formatted_address = address_info.get(
201
+ 'formattedAddress', 'Not available'
202
+ )
203
+ location = geocode.get('location', {})
204
+ latitude = location.get('latitude', 'Not available')
205
+ longitude = location.get('longitude', 'Not available')
206
+ true_metadata_types = [
207
+ key for key, value in metadata.items() if value
208
+ ]
209
+ true_metadata_types_str = (
210
+ ', '.join(true_metadata_types)
211
+ if true_metadata_types
212
+ else 'None'
213
+ )
214
+
215
+ description = (
216
+ f"Address completion status: {address_complete}. "
217
+ f"Formatted address: {formatted_address}. "
218
+ f"Location (latitude, longitude): ({latitude}, {longitude}). "
219
+ f"Metadata indicating true types: {true_metadata_types_str}."
220
+ )
221
+
222
+ return description
223
+ except Exception as e:
224
+ return f"An unexpected error occurred: {e!s}"
225
+
226
+ @handle_googlemaps_exceptions
227
+ def get_elevation(self, lat_lng: Tuple) -> str:
228
+ r"""Retrieves elevation data for a given latitude and longitude.
229
+
230
+ Uses the Google Maps API to fetch elevation data for the specified
231
+ latitude and longitude. It handles exceptions gracefully and returns a
232
+ description of the elevation, including its value in meters and the
233
+ data resolution.
234
+
235
+ Args:
236
+ lat_lng (Tuple[float, float]): The latitude and longitude for
237
+ which to retrieve elevation data.
238
+
239
+ Returns:
240
+ str: A description of the elevation at the specified location(s),
241
+ including the elevation in meters and the data resolution. If
242
+ elevation data is not available, a message indicating this is
243
+ returned.
244
+ """
245
+ googlemaps = self._import_googlemaps_or_raise()
246
+ google_maps_api_key = self._get_googlemaps_api_key()
247
+ try:
248
+ gmaps = googlemaps.Client(key=google_maps_api_key)
249
+ except Exception as e:
250
+ return f"Error: {e!s}"
251
+
252
+ # Assuming gmaps is a configured Google Maps client instance
253
+ elevation_result = gmaps.elevation(lat_lng)
254
+
255
+ # Extract the elevation data from the first
256
+ # (and presumably only) result
257
+ if elevation_result:
258
+ elevation = elevation_result[0]['elevation']
259
+ location = elevation_result[0]['location']
260
+ resolution = elevation_result[0]['resolution']
261
+
262
+ # Format the elevation data into a natural language description
263
+ description = (
264
+ f"The elevation at latitude {location['lat']}, "
265
+ f"longitude {location['lng']} "
266
+ f"is approximately {elevation:.2f} meters above sea level, "
267
+ f"with a data resolution of {resolution:.2f} meters."
268
+ )
269
+ else:
270
+ description = (
271
+ "Elevation data is not available for the given location."
272
+ )
273
+
274
+ return description
275
+
276
+ def _format_offset_to_natural_language(self, offset: int) -> str:
277
+ r"""Converts a time offset in seconds to a more natural language
278
+ description using hours as the unit, with decimal places to represent
279
+ minutes and seconds.
280
+
281
+ Args:
282
+ offset (int): The time offset in seconds. Can be positive,
283
+ negative, or zero.
284
+
285
+ Returns:
286
+ str: A string representing the offset in hours, such as
287
+ "+2.50 hours" or "-3.75 hours".
288
+ """
289
+ # Convert the offset to hours as a float
290
+ hours = offset / 3600.0
291
+ hours_str = f"{hours:+.2f} hour{'s' if abs(hours) != 1 else ''}"
292
+ return hours_str
293
+
294
+ @handle_googlemaps_exceptions
295
+ def get_timezone(self, lat_lng: Tuple) -> str:
296
+ r"""Retrieves timezone information for a given latitude and longitude.
297
+
298
+ This function uses the Google Maps Timezone API to fetch timezone
299
+ data for the specified latitude and longitude. It returns a natural
300
+ language description of the timezone, including the timezone ID, name,
301
+ standard time offset, daylight saving time offset, and the total
302
+ offset from Coordinated Universal Time (UTC).
303
+
304
+ Args:
305
+ lat_lng (Tuple[float, float]): The latitude and longitude for
306
+ which to retrieve elevation data.
307
+
308
+ Returns:
309
+ str: A descriptive string of the timezone information,
310
+ including the timezone ID and name, standard time offset,
311
+ daylight saving time offset, and total offset from UTC.
312
+ """
313
+ googlemaps = self._import_googlemaps_or_raise()
314
+ google_maps_api_key = self._get_googlemaps_api_key()
315
+ try:
316
+ gmaps = googlemaps.Client(key=google_maps_api_key)
317
+ except Exception as e:
318
+ return f"Error: {e!s}"
319
+
320
+ # Get timezone information
321
+ timezone_dict = gmaps.timezone(lat_lng)
322
+
323
+ # Extract necessary information
324
+ dst_offset = timezone_dict[
325
+ 'dstOffset'
326
+ ] # Daylight Saving Time offset in seconds
327
+ raw_offset = timezone_dict[
328
+ 'rawOffset'
329
+ ] # Standard time offset in seconds
330
+ timezone_id = timezone_dict['timeZoneId']
331
+ timezone_name = timezone_dict['timeZoneName']
332
+
333
+ raw_offset_str = self._format_offset_to_natural_language(raw_offset)
334
+ dst_offset_str = self._format_offset_to_natural_language(dst_offset)
335
+ total_offset_seconds = dst_offset + raw_offset
336
+ total_offset_str = self._format_offset_to_natural_language(
337
+ total_offset_seconds
338
+ )
339
+
340
+ # Create a natural language description
341
+ description = (
342
+ f"Timezone ID is {timezone_id}, named {timezone_name}. "
343
+ f"The standard time offset is {raw_offset_str}. "
344
+ f"Daylight Saving Time offset is {dst_offset_str}. "
345
+ f"The total offset from Coordinated Universal Time (UTC) is "
346
+ f"{total_offset_str}, including any "
347
+ "Daylight Saving Time adjustment "
348
+ f"if applicable. "
349
+ )
350
+
351
+ return description
352
+
353
+ def get_tools(self) -> List[OpenAIFunction]:
354
+ r"""Returns a list of OpenAIFunction objects representing the
355
+ functions in the toolkit.
356
+
357
+ Returns:
358
+ List[OpenAIFunction]: A list of OpenAIFunction objects
359
+ representing the functions in the toolkit.
360
+ """
361
+ return [
362
+ OpenAIFunction(self.get_address_description),
363
+ OpenAIFunction(self.get_elevation),
364
+ OpenAIFunction(self.get_timezone),
365
+ ]
366
+
367
+
368
+ MAP_FUNCS: List[OpenAIFunction] = GoogleMapsToolkit().get_tools()
@@ -0,0 +1,79 @@
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
+
15
+ from typing import List
16
+
17
+ from camel.toolkits.base import BaseToolkit
18
+ from camel.toolkits.openai_function import OpenAIFunction
19
+
20
+
21
+ class MathToolkit(BaseToolkit):
22
+ r"""A class representing a toolkit for mathematical operations.
23
+
24
+ This class provides methods for basic mathematical operations such as
25
+ addition, subtraction, and multiplication.
26
+ """
27
+
28
+ def add(self, a: int, b: int) -> int:
29
+ r"""Adds two numbers.
30
+
31
+ Args:
32
+ a (int): The first number to be added.
33
+ b (int): The second number to be added.
34
+
35
+ Returns:
36
+ integer: The sum of the two numbers.
37
+ """
38
+ return a + b
39
+
40
+ def sub(self, a: int, b: int) -> int:
41
+ r"""Do subtraction between two numbers.
42
+
43
+ Args:
44
+ a (int): The minuend in subtraction.
45
+ b (int): The subtrahend in subtraction.
46
+
47
+ Returns:
48
+ integer: The result of subtracting :obj:`b` from :obj:`a`.
49
+ """
50
+ return a - b
51
+
52
+ def mul(self, a: int, b: int) -> int:
53
+ r"""Multiplies two integers.
54
+
55
+ Args:
56
+ a (int): The multiplier in the multiplication.
57
+ b (int): The multiplicand in the multiplication.
58
+
59
+ Returns:
60
+ integer: The product of the two numbers.
61
+ """
62
+ return a * b
63
+
64
+ def get_tools(self) -> List[OpenAIFunction]:
65
+ r"""Returns a list of OpenAIFunction objects representing the
66
+ functions in the toolkit.
67
+
68
+ Returns:
69
+ List[OpenAIFunction]: A list of OpenAIFunction objects
70
+ representing the functions in the toolkit.
71
+ """
72
+ return [
73
+ OpenAIFunction(self.add),
74
+ OpenAIFunction(self.sub),
75
+ OpenAIFunction(self.mul),
76
+ ]
77
+
78
+
79
+ MATH_FUNCS: List[OpenAIFunction] = MathToolkit().get_tools()