camel-ai 0.2.67__py3-none-any.whl → 0.2.80a2__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.
Files changed (224) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/_types.py +6 -2
  3. camel/agents/_utils.py +38 -0
  4. camel/agents/chat_agent.py +4014 -410
  5. camel/agents/mcp_agent.py +30 -27
  6. camel/agents/repo_agent.py +2 -1
  7. camel/benchmarks/browsecomp.py +6 -6
  8. camel/configs/__init__.py +15 -0
  9. camel/configs/aihubmix_config.py +88 -0
  10. camel/configs/amd_config.py +70 -0
  11. camel/configs/cometapi_config.py +104 -0
  12. camel/configs/minimax_config.py +93 -0
  13. camel/configs/nebius_config.py +103 -0
  14. camel/configs/vllm_config.py +2 -0
  15. camel/data_collectors/alpaca_collector.py +15 -6
  16. camel/datagen/self_improving_cot.py +1 -1
  17. camel/datasets/base_generator.py +39 -10
  18. camel/environments/__init__.py +12 -0
  19. camel/environments/rlcards_env.py +860 -0
  20. camel/environments/single_step.py +28 -3
  21. camel/environments/tic_tac_toe.py +1 -1
  22. camel/interpreters/__init__.py +2 -0
  23. camel/interpreters/docker/Dockerfile +4 -16
  24. camel/interpreters/docker_interpreter.py +3 -2
  25. camel/interpreters/e2b_interpreter.py +34 -1
  26. camel/interpreters/internal_python_interpreter.py +51 -2
  27. camel/interpreters/microsandbox_interpreter.py +395 -0
  28. camel/loaders/__init__.py +11 -2
  29. camel/loaders/base_loader.py +85 -0
  30. camel/loaders/chunkr_reader.py +9 -0
  31. camel/loaders/firecrawl_reader.py +4 -4
  32. camel/logger.py +1 -1
  33. camel/memories/agent_memories.py +84 -1
  34. camel/memories/base.py +34 -0
  35. camel/memories/blocks/chat_history_block.py +122 -4
  36. camel/memories/blocks/vectordb_block.py +8 -1
  37. camel/memories/context_creators/score_based.py +29 -237
  38. camel/memories/records.py +88 -8
  39. camel/messages/base.py +166 -40
  40. camel/messages/func_message.py +32 -5
  41. camel/models/__init__.py +10 -0
  42. camel/models/aihubmix_model.py +83 -0
  43. camel/models/aiml_model.py +1 -16
  44. camel/models/amd_model.py +101 -0
  45. camel/models/anthropic_model.py +117 -18
  46. camel/models/aws_bedrock_model.py +2 -33
  47. camel/models/azure_openai_model.py +205 -91
  48. camel/models/base_audio_model.py +3 -1
  49. camel/models/base_model.py +189 -24
  50. camel/models/cohere_model.py +5 -17
  51. camel/models/cometapi_model.py +83 -0
  52. camel/models/crynux_model.py +1 -16
  53. camel/models/deepseek_model.py +6 -16
  54. camel/models/fish_audio_model.py +6 -0
  55. camel/models/gemini_model.py +71 -20
  56. camel/models/groq_model.py +1 -17
  57. camel/models/internlm_model.py +1 -16
  58. camel/models/litellm_model.py +49 -32
  59. camel/models/lmstudio_model.py +1 -17
  60. camel/models/minimax_model.py +83 -0
  61. camel/models/mistral_model.py +1 -16
  62. camel/models/model_factory.py +27 -1
  63. camel/models/model_manager.py +24 -6
  64. camel/models/modelscope_model.py +1 -16
  65. camel/models/moonshot_model.py +185 -19
  66. camel/models/nebius_model.py +83 -0
  67. camel/models/nemotron_model.py +0 -5
  68. camel/models/netmind_model.py +1 -16
  69. camel/models/novita_model.py +1 -16
  70. camel/models/nvidia_model.py +1 -16
  71. camel/models/ollama_model.py +4 -19
  72. camel/models/openai_compatible_model.py +171 -46
  73. camel/models/openai_model.py +205 -77
  74. camel/models/openrouter_model.py +1 -17
  75. camel/models/ppio_model.py +1 -16
  76. camel/models/qianfan_model.py +1 -16
  77. camel/models/qwen_model.py +1 -16
  78. camel/models/reka_model.py +1 -16
  79. camel/models/samba_model.py +34 -47
  80. camel/models/sglang_model.py +64 -31
  81. camel/models/siliconflow_model.py +1 -16
  82. camel/models/stub_model.py +0 -4
  83. camel/models/togetherai_model.py +1 -16
  84. camel/models/vllm_model.py +1 -16
  85. camel/models/volcano_model.py +0 -17
  86. camel/models/watsonx_model.py +1 -16
  87. camel/models/yi_model.py +1 -16
  88. camel/models/zhipuai_model.py +60 -16
  89. camel/parsers/__init__.py +18 -0
  90. camel/parsers/mcp_tool_call_parser.py +176 -0
  91. camel/retrievers/auto_retriever.py +1 -0
  92. camel/runtimes/configs.py +11 -11
  93. camel/runtimes/daytona_runtime.py +15 -16
  94. camel/runtimes/docker_runtime.py +6 -6
  95. camel/runtimes/remote_http_runtime.py +5 -5
  96. camel/services/agent_openapi_server.py +380 -0
  97. camel/societies/__init__.py +2 -0
  98. camel/societies/role_playing.py +26 -28
  99. camel/societies/workforce/__init__.py +2 -0
  100. camel/societies/workforce/events.py +122 -0
  101. camel/societies/workforce/prompts.py +249 -38
  102. camel/societies/workforce/role_playing_worker.py +82 -20
  103. camel/societies/workforce/single_agent_worker.py +634 -34
  104. camel/societies/workforce/structured_output_handler.py +512 -0
  105. camel/societies/workforce/task_channel.py +169 -23
  106. camel/societies/workforce/utils.py +176 -9
  107. camel/societies/workforce/worker.py +77 -23
  108. camel/societies/workforce/workflow_memory_manager.py +772 -0
  109. camel/societies/workforce/workforce.py +3168 -478
  110. camel/societies/workforce/workforce_callback.py +74 -0
  111. camel/societies/workforce/workforce_logger.py +203 -175
  112. camel/societies/workforce/workforce_metrics.py +33 -0
  113. camel/storages/__init__.py +4 -0
  114. camel/storages/key_value_storages/json.py +15 -2
  115. camel/storages/key_value_storages/mem0_cloud.py +48 -47
  116. camel/storages/object_storages/google_cloud.py +1 -1
  117. camel/storages/vectordb_storages/__init__.py +6 -0
  118. camel/storages/vectordb_storages/chroma.py +731 -0
  119. camel/storages/vectordb_storages/oceanbase.py +13 -13
  120. camel/storages/vectordb_storages/pgvector.py +349 -0
  121. camel/storages/vectordb_storages/qdrant.py +3 -3
  122. camel/storages/vectordb_storages/surreal.py +365 -0
  123. camel/storages/vectordb_storages/tidb.py +8 -6
  124. camel/tasks/task.py +244 -27
  125. camel/toolkits/__init__.py +46 -8
  126. camel/toolkits/aci_toolkit.py +64 -19
  127. camel/toolkits/arxiv_toolkit.py +6 -6
  128. camel/toolkits/base.py +63 -5
  129. camel/toolkits/code_execution.py +28 -1
  130. camel/toolkits/context_summarizer_toolkit.py +684 -0
  131. camel/toolkits/craw4ai_toolkit.py +93 -0
  132. camel/toolkits/dappier_toolkit.py +10 -6
  133. camel/toolkits/dingtalk.py +1135 -0
  134. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  135. camel/toolkits/excel_toolkit.py +901 -67
  136. camel/toolkits/file_toolkit.py +1402 -0
  137. camel/toolkits/function_tool.py +30 -6
  138. camel/toolkits/github_toolkit.py +107 -20
  139. camel/toolkits/gmail_toolkit.py +1839 -0
  140. camel/toolkits/google_calendar_toolkit.py +38 -4
  141. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  142. camel/toolkits/human_toolkit.py +34 -10
  143. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  144. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  145. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  146. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
  147. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  148. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +3749 -0
  149. camel/toolkits/hybrid_browser_toolkit/ts/package.json +32 -0
  150. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  151. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1815 -0
  152. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  153. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +590 -0
  154. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  155. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  156. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  157. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  158. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +130 -0
  159. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +26 -0
  160. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
  161. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1032 -0
  162. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  163. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  164. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  165. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  166. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  167. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  168. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  169. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  170. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  171. camel/toolkits/image_generation_toolkit.py +390 -0
  172. camel/toolkits/jina_reranker_toolkit.py +3 -4
  173. camel/toolkits/klavis_toolkit.py +5 -1
  174. camel/toolkits/markitdown_toolkit.py +104 -0
  175. camel/toolkits/math_toolkit.py +64 -10
  176. camel/toolkits/mcp_toolkit.py +370 -45
  177. camel/toolkits/memory_toolkit.py +5 -1
  178. camel/toolkits/message_agent_toolkit.py +608 -0
  179. camel/toolkits/message_integration.py +724 -0
  180. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  181. camel/toolkits/note_taking_toolkit.py +277 -0
  182. camel/toolkits/notion_mcp_toolkit.py +224 -0
  183. camel/toolkits/openbb_toolkit.py +5 -1
  184. camel/toolkits/origene_mcp_toolkit.py +56 -0
  185. camel/toolkits/playwright_mcp_toolkit.py +12 -31
  186. camel/toolkits/pptx_toolkit.py +25 -12
  187. camel/toolkits/resend_toolkit.py +168 -0
  188. camel/toolkits/screenshot_toolkit.py +213 -0
  189. camel/toolkits/search_toolkit.py +437 -142
  190. camel/toolkits/slack_toolkit.py +104 -50
  191. camel/toolkits/sympy_toolkit.py +1 -1
  192. camel/toolkits/task_planning_toolkit.py +3 -3
  193. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  194. camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
  195. camel/toolkits/terminal_toolkit/utils.py +532 -0
  196. camel/toolkits/thinking_toolkit.py +1 -1
  197. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  198. camel/toolkits/video_analysis_toolkit.py +106 -26
  199. camel/toolkits/video_download_toolkit.py +17 -14
  200. camel/toolkits/web_deploy_toolkit.py +1219 -0
  201. camel/toolkits/wechat_official_toolkit.py +483 -0
  202. camel/toolkits/zapier_toolkit.py +5 -1
  203. camel/types/__init__.py +2 -2
  204. camel/types/agents/tool_calling_record.py +4 -1
  205. camel/types/enums.py +316 -40
  206. camel/types/openai_types.py +2 -2
  207. camel/types/unified_model_type.py +31 -4
  208. camel/utils/commons.py +36 -5
  209. camel/utils/constants.py +3 -0
  210. camel/utils/context_utils.py +1003 -0
  211. camel/utils/mcp.py +138 -4
  212. camel/utils/mcp_client.py +45 -1
  213. camel/utils/message_summarizer.py +148 -0
  214. camel/utils/token_counting.py +43 -20
  215. camel/utils/tool_result.py +44 -0
  216. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +296 -85
  217. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +219 -146
  218. camel/loaders/pandas_reader.py +0 -368
  219. camel/toolkits/dalle_toolkit.py +0 -175
  220. camel/toolkits/file_write_toolkit.py +0 -444
  221. camel/toolkits/openai_agent_toolkit.py +0 -135
  222. camel/toolkits/terminal_toolkit.py +0 -1037
  223. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
  224. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
@@ -17,7 +17,11 @@ from typing import List, Literal, Optional
17
17
 
18
18
  from camel.toolkits.base import BaseToolkit
19
19
  from camel.toolkits.function_tool import FunctionTool
20
- from camel.utils import MCPServer, api_keys_required, dependencies_required
20
+ from camel.utils import (
21
+ MCPServer,
22
+ api_keys_required,
23
+ dependencies_required,
24
+ )
21
25
 
22
26
 
23
27
  @MCPServer()
@@ -0,0 +1,56 @@
1
+ # ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+ from typing import Dict, Optional
16
+
17
+ from .mcp_toolkit import MCPToolkit
18
+
19
+
20
+ class OrigeneToolkit(MCPToolkit):
21
+ r"""OrigeneToolkit provides an interface for interacting with
22
+ Origene MCP server.
23
+
24
+ This toolkit can be used as an async context manager for automatic
25
+ connection management:
26
+
27
+ async with OrigeneToolkit(config_dict=config) as toolkit:
28
+ tools = toolkit.get_tools()
29
+ # Toolkit is automatically disconnected when exiting
30
+
31
+ Attributes:
32
+ config_dict (Dict): Configuration dictionary for MCP servers.
33
+ timeout (Optional[float]): Connection timeout in seconds.
34
+ (default: :obj:`None`)
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ config_dict: Optional[Dict] = None,
40
+ timeout: Optional[float] = None,
41
+ ) -> None:
42
+ r"""Initializes the OrigeneToolkit.
43
+
44
+ Args:
45
+ config_dict (Optional[Dict]): Configuration dictionary for MCP
46
+ servers. If None, raises ValueError as configuration is
47
+ required. (default: :obj:`None`)
48
+ timeout (Optional[float]): Connection timeout in seconds.
49
+ (default: :obj:`None`)
50
+ """
51
+ # Validate that config_dict is provided
52
+ if config_dict is None:
53
+ raise ValueError("config_dict must be provided")
54
+
55
+ # Initialize parent MCPToolkit with provided configuration
56
+ super().__init__(config_dict=config_dict, timeout=timeout)
@@ -14,12 +14,10 @@
14
14
 
15
15
  from typing import List, Optional
16
16
 
17
- from camel.toolkits import BaseToolkit, FunctionTool
18
-
19
17
  from .mcp_toolkit import MCPToolkit
20
18
 
21
19
 
22
- class PlaywrightMCPToolkit(BaseToolkit):
20
+ class PlaywrightMCPToolkit(MCPToolkit):
23
21
  r"""PlaywrightMCPToolkit provides an interface for interacting with web
24
22
  browsers using the Playwright automation library through the Model Context
25
23
  Protocol (MCP).
@@ -51,33 +49,16 @@ class PlaywrightMCPToolkit(BaseToolkit):
51
49
  `["--cdp-endpoint=http://localhost:9222"]`.
52
50
  (default: :obj:`None`)
53
51
  """
54
- super().__init__(timeout=timeout)
55
-
56
- self._mcp_toolkit = MCPToolkit(
57
- config_dict={
58
- "mcpServers": {
59
- "playwright": {
60
- "command": "npx",
61
- "args": ["@playwright/mcp@latest"]
62
- + (additional_args or []),
63
- }
52
+ # Create config for Playwright MCP server
53
+ config_dict = {
54
+ "mcpServers": {
55
+ "playwright": {
56
+ "command": "npx",
57
+ "args": ["@playwright/mcp@latest"]
58
+ + (additional_args or []),
64
59
  }
65
- },
66
- timeout=timeout,
67
- )
68
-
69
- async def connect(self):
70
- r"""Explicitly connect to the Playwright MCP server."""
71
- await self._mcp_toolkit.connect()
60
+ }
61
+ }
72
62
 
73
- async def disconnect(self):
74
- r"""Explicitly disconnect from the Playwright MCP server."""
75
- await self._mcp_toolkit.disconnect()
76
-
77
- def get_tools(self) -> List[FunctionTool]:
78
- r"""Returns a list of tools provided by the PlaywrightMCPToolkit.
79
-
80
- Returns:
81
- List[FunctionTool]: List of available tools.
82
- """
83
- return self._mcp_toolkit.get_tools()
63
+ # Initialize parent MCPToolkit with Playwright configuration
64
+ super().__init__(config_dict=config_dict, timeout=timeout)
@@ -53,22 +53,35 @@ class PPTXToolkit(BaseToolkit):
53
53
 
54
54
  def __init__(
55
55
  self,
56
- output_dir: str = "./",
56
+ working_directory: Optional[str] = None,
57
57
  timeout: Optional[float] = None,
58
58
  ) -> None:
59
59
  r"""Initialize the PPTXToolkit.
60
60
 
61
61
  Args:
62
- output_dir (str): The default directory for output files.
63
- Defaults to the current working directory.
62
+ working_directory (str, optional): The default directory for
63
+ output files. If not provided, it will be determined by the
64
+ `CAMEL_WORKDIR` environment variable (if set). If the
65
+ environment variable is not set, it defaults to
66
+ `camel_working_dir`.
64
67
  timeout (Optional[float]): The timeout for the toolkit.
65
- (default: :obj: `None`)
68
+ (default: :obj:`None`)
66
69
  """
67
70
  super().__init__(timeout=timeout)
68
- self.output_dir = Path(output_dir).resolve()
69
- self.output_dir.mkdir(parents=True, exist_ok=True)
71
+
72
+ if working_directory:
73
+ self.working_directory = Path(working_directory).resolve()
74
+ else:
75
+ camel_workdir = os.environ.get("CAMEL_WORKDIR")
76
+ if camel_workdir:
77
+ self.working_directory = Path(camel_workdir).resolve()
78
+ else:
79
+ self.working_directory = Path("./camel_working_dir").resolve()
80
+
81
+ self.working_directory.mkdir(parents=True, exist_ok=True)
70
82
  logger.info(
71
- f"PPTXToolkit initialized with output directory: {self.output_dir}"
83
+ f"PPTXToolkit initialized with output directory: "
84
+ f"{self.working_directory}"
72
85
  )
73
86
 
74
87
  def _resolve_filepath(self, file_path: str) -> Path:
@@ -87,7 +100,7 @@ class PPTXToolkit(BaseToolkit):
87
100
  """
88
101
  path_obj = Path(file_path)
89
102
  if not path_obj.is_absolute():
90
- path_obj = self.output_dir / path_obj
103
+ path_obj = self.working_directory / path_obj
91
104
 
92
105
  sanitized_filename = self._sanitize_filename(path_obj.name)
93
106
  path_obj = path_obj.parent / sanitized_filename
@@ -120,7 +133,7 @@ class PPTXToolkit(BaseToolkit):
120
133
  frame_paragraph: The paragraph to format.
121
134
  text (str): The text to format.
122
135
  set_color_to_white (bool): Whether to set the color to white.
123
- (default: :obj: `False`)
136
+ (default: :obj:`False`)
124
137
  """
125
138
  from pptx.dml.color import RGBColor
126
139
 
@@ -170,7 +183,7 @@ class PPTXToolkit(BaseToolkit):
170
183
  flat_items_list (List[Tuple[str, int]]): The list of items to be
171
184
  displayed.
172
185
  set_color_to_white (bool): Whether to set the font color to white.
173
- (default: :obj: `False`)
186
+ (default: :obj:`False`)
174
187
  """
175
188
  if not flat_items_list:
176
189
  logger.warning("Empty bullet point list provided")
@@ -365,10 +378,10 @@ class PPTXToolkit(BaseToolkit):
365
378
  * Table slides: {"heading": str, "table": {"headers": list
366
379
  of str, "rows": list of list of str}}
367
380
  filename (str): The name or path of the file. If a relative path is
368
- supplied, it is resolved to self.output_dir.
381
+ supplied, it is resolved to self.working_directory.
369
382
  template (Optional[str]): The path to the template PPTX file.
370
383
  Initializes a presentation from a given template file Or PPTX
371
- file. (default: :obj: `None`)
384
+ file. (default: :obj:`None`)
372
385
 
373
386
  Returns:
374
387
  str: A success message indicating the file was created.
@@ -0,0 +1,168 @@
1
+ # ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+ from typing import Dict, List, Optional, cast
16
+
17
+ from camel.toolkits.base import BaseToolkit
18
+ from camel.toolkits.function_tool import FunctionTool
19
+ from camel.utils import MCPServer, api_keys_required
20
+
21
+
22
+ @MCPServer()
23
+ class ResendToolkit(BaseToolkit):
24
+ r"""A toolkit for sending emails using the Resend API.
25
+
26
+ This toolkit provides functionality to send emails using Resend's
27
+ Python SDK.It supports sending both HTML and plain text emails,
28
+ with options for multiple recipients, CC, BCC, reply-to
29
+ addresses, and custom headers.
30
+
31
+ Notes:
32
+ To use this toolkit, you need to set the following environment
33
+ variable:
34
+ - RESEND_API_KEY: Your Resend API key. You can get one from
35
+ https://resend.com/api-keys
36
+
37
+ Example:
38
+ .. code-block:: python
39
+
40
+ from camel.toolkits import ResendToolkit
41
+
42
+ # Initialize the toolkit
43
+ toolkit = ResendToolkit()
44
+
45
+ # Get tools
46
+ tools = toolkit.get_tools()
47
+ """
48
+
49
+ @api_keys_required([(None, "RESEND_API_KEY")])
50
+ def send_email(
51
+ self,
52
+ to: List[str],
53
+ subject: str,
54
+ from_email: str,
55
+ html: Optional[str] = None,
56
+ text: Optional[str] = None,
57
+ cc: Optional[List[str]] = None,
58
+ bcc: Optional[List[str]] = None,
59
+ reply_to: Optional[str] = None,
60
+ tags: Optional[List[Dict[str, str]]] = None,
61
+ headers: Optional[Dict[str, str]] = None,
62
+ ) -> str:
63
+ r"""Send an email using the Resend API.
64
+
65
+ Args:
66
+ to (List[str]): List of recipient email addresses.
67
+ subject (str): The email subject line.
68
+ from_email (str): The sender email address. Must be from a verified
69
+ domain.
70
+ html (Optional[str]): The HTML content of the email. Either html or
71
+ text must be provided. (default: :obj:`None`)
72
+ text (Optional[str]): The plain text content of the email. Either
73
+ html or text must be provided. (default: :obj:`None`)
74
+ cc (Optional[List[str]]): List of CC recipient email addresses.
75
+ (default: :obj:`None`)
76
+ bcc (Optional[List[str]]): List of BCC recipient email addresses.
77
+ (default: :obj:`None`)
78
+ reply_to (Optional[str]): The reply-to email address.
79
+ (default: :obj:`None`)
80
+ tags (Optional[List[Dict[str, str]]]): List of tags to attach to
81
+ the email. Each tag should be a dict with 'name' and
82
+ 'value' keys. (default: :obj:`None`)
83
+ headers (Optional[Dict[str, str]]): Custom headers to include in
84
+ the email.(default: :obj:`None`)
85
+
86
+ Returns:
87
+ str: A success message with the email ID if sent successfully,
88
+ or an error message if the send failed.
89
+
90
+ Raises:
91
+ ValueError: If neither html nor text content is provided.
92
+
93
+ Example:
94
+ .. code-block:: python
95
+
96
+ toolkit = ResendToolkit()
97
+ result = toolkit.send_email(
98
+ to=["recipient@example.com"],
99
+ subject="Hello World",
100
+ from_email="sender@yourdomain.com",
101
+ html="<h1>Hello, World!</h1>",
102
+ text="Hello, World!"
103
+ )
104
+ """
105
+ import os
106
+
107
+ if not html and not text:
108
+ raise ValueError(
109
+ "Either 'html' or 'text' content must be provided"
110
+ )
111
+
112
+ try:
113
+ import resend
114
+ except ImportError:
115
+ raise ImportError(
116
+ "Please install the resend package first. "
117
+ "You can install it by running `pip install resend`."
118
+ )
119
+
120
+ # Set the API key
121
+ resend.api_key = os.getenv("RESEND_API_KEY")
122
+
123
+ # Prepare email parameters
124
+ params: resend.Emails.SendParams = {
125
+ "from": from_email,
126
+ "to": to,
127
+ "subject": subject,
128
+ }
129
+
130
+ # Add content
131
+ if html:
132
+ params["html"] = html
133
+ if text:
134
+ params["text"] = text
135
+
136
+ # Add optional parameters
137
+ if cc:
138
+ params["cc"] = cc
139
+ if bcc:
140
+ params["bcc"] = bcc
141
+ if reply_to:
142
+ params["reply_to"] = reply_to
143
+ if tags:
144
+ params["tags"] = cast('list[resend.emails._tag.Tag]', tags)
145
+ if headers:
146
+ params["headers"] = headers
147
+
148
+ try:
149
+ # Send the email
150
+ email = resend.Emails.send(params)
151
+ return (
152
+ f"Email sent successfully. "
153
+ f"Email ID: {email.get('id', 'Unknown')}"
154
+ )
155
+ except Exception as e:
156
+ return f"Failed to send email: {e!s}"
157
+
158
+ def get_tools(self) -> List[FunctionTool]:
159
+ r"""Returns a list of FunctionTool objects representing the
160
+ functions in the toolkit.
161
+
162
+ Returns:
163
+ List[FunctionTool]: A list of FunctionTool objects
164
+ representing the functions in the toolkit.
165
+ """
166
+ return [
167
+ FunctionTool(self.send_email),
168
+ ]
@@ -0,0 +1,213 @@
1
+ # ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+ import os
16
+ from pathlib import Path
17
+ from typing import List, Optional
18
+
19
+ from PIL import Image
20
+
21
+ from camel.logger import get_logger
22
+ from camel.messages import BaseMessage
23
+ from camel.toolkits import BaseToolkit, FunctionTool
24
+ from camel.toolkits.base import RegisteredAgentToolkit
25
+ from camel.utils import dependencies_required
26
+
27
+ logger = get_logger(__name__)
28
+
29
+
30
+ class ScreenshotToolkit(BaseToolkit, RegisteredAgentToolkit):
31
+ r"""A toolkit for taking screenshots."""
32
+
33
+ @dependencies_required('PIL')
34
+ def __init__(
35
+ self,
36
+ working_directory: Optional[str] = None,
37
+ timeout: Optional[float] = None,
38
+ ):
39
+ r"""Initializes the ScreenshotToolkit.
40
+
41
+ Args:
42
+ working_directory (str, optional): The directory path where notes
43
+ will be stored. If not provided, it will be determined by the
44
+ `CAMEL_WORKDIR` environment variable (if set). If the
45
+ environment variable is not set, it defaults to
46
+ `camel_working_dir`.
47
+ timeout (Optional[float]): Timeout for API requests in seconds.
48
+ (default: :obj:`None`)
49
+ """
50
+ from PIL import ImageGrab
51
+
52
+ super().__init__(timeout=timeout)
53
+ RegisteredAgentToolkit.__init__(self)
54
+
55
+ camel_workdir = os.environ.get("CAMEL_WORKDIR")
56
+ if working_directory:
57
+ path = Path(working_directory)
58
+ elif camel_workdir:
59
+ path = Path(camel_workdir)
60
+ else:
61
+ path = Path("camel_working_dir")
62
+
63
+ self.ImageGrab = ImageGrab
64
+ self.screenshots_dir = path
65
+ self.screenshots_dir.mkdir(parents=True, exist_ok=True)
66
+
67
+ def read_image(
68
+ self,
69
+ image_path: str,
70
+ instruction: str = "",
71
+ ) -> str:
72
+ r"""Analyzes an image from a local file path.
73
+
74
+ This function enables you to "see" and interpret an image from a
75
+ file. It's useful for tasks where you need to understand visual
76
+ information, such as reading a screenshot of a webpage or a diagram.
77
+
78
+ Args:
79
+ image_path (str): The local file path to the image.
80
+ For example: 'screenshots/login_page.png'.
81
+ instruction (str, optional): Specific instructions for what to look
82
+ for or what to do with the image. For example: "What is the
83
+ main headline on this page?" or "Find the 'Submit' button.".
84
+
85
+ Returns:
86
+ str: The response after analyzing the image, which could be a
87
+ description, an answer, or a confirmation of an action.
88
+ """
89
+ if self.agent is None:
90
+ logger.error(
91
+ "Cannot record screenshot in memory: No agent registered. "
92
+ "Please pass this toolkit to ChatAgent via "
93
+ "toolkits_to_register_agent parameter."
94
+ )
95
+ return (
96
+ "Error: No agent registered. Please pass this toolkit to "
97
+ "ChatAgent via toolkits_to_register_agent parameter."
98
+ )
99
+
100
+ try:
101
+ image_path = str(Path(image_path).absolute())
102
+
103
+ # Check if file exists before trying to open
104
+ if not os.path.exists(image_path):
105
+ error_msg = f"Screenshot file not found: {image_path}"
106
+ logger.error(error_msg)
107
+ return f"Error: {error_msg}"
108
+
109
+ # Load the image from the path
110
+ img = Image.open(image_path)
111
+
112
+ # Create a message with the screenshot image
113
+ message = BaseMessage.make_user_message(
114
+ role_name="User",
115
+ content=instruction,
116
+ image_list=[img],
117
+ )
118
+
119
+ # Record the message in agent's memory
120
+ response = self.agent.step(message)
121
+ return response.msgs[0].content
122
+
123
+ except Exception as e:
124
+ logger.error(f"Error reading screenshot: {e}")
125
+ return f"Error reading screenshot: {e}"
126
+
127
+ def take_screenshot_and_read_image(
128
+ self,
129
+ filename: str,
130
+ save_to_file: bool = True,
131
+ read_image: bool = True,
132
+ instruction: Optional[str] = None,
133
+ ) -> str:
134
+ r"""Captures a screenshot of the entire screen.
135
+
136
+ This function can save the screenshot to a file and optionally analyze
137
+ it. It's useful for capturing the current state of the UI for
138
+ documentation, analysis, or to guide subsequent actions.
139
+
140
+ Args:
141
+ filename (str): The name for the screenshot file (e.g.,
142
+ "homepage.png"). The file is saved in a `screenshots`
143
+ subdirectory within the working directory. Must end with
144
+ `.png`. (default: :obj:`None`)
145
+ save_to_file (bool, optional): If `True`, saves the screenshot to
146
+ a file. (default: :obj:`True`)
147
+ read_image (bool, optional): If `True`, the agent will analyze
148
+ the screenshot. `save_to_file` must also be `True`.
149
+ (default: :obj:`True`)
150
+ instruction (Optional[str], optional): A specific question or
151
+ command for the agent regarding the screenshot, used only if
152
+ `read_image` is `True`. For example: "Confirm that the
153
+ user is logged in.".
154
+
155
+ Returns:
156
+ str: A confirmation message indicating success or failure,
157
+ including the file path if saved, and the agent's response
158
+ if `read_image` is `True`.
159
+ """
160
+ try:
161
+ # Take screenshot of entire screen
162
+ screenshot = self.ImageGrab.grab()
163
+
164
+ # Save to file if requested
165
+ file_path = None
166
+ if save_to_file:
167
+ # Create directory if it doesn't exist
168
+ os.makedirs(self.screenshots_dir, exist_ok=True)
169
+
170
+ # Create unique filename if file already exists
171
+ base_path = os.path.join(self.screenshots_dir, filename)
172
+ file_path = base_path
173
+ counter = 1
174
+ while os.path.exists(file_path):
175
+ name, ext = os.path.splitext(filename)
176
+ unique_filename = f"{name}_{counter}{ext}"
177
+ file_path = os.path.join(
178
+ self.screenshots_dir, unique_filename
179
+ )
180
+ counter += 1
181
+
182
+ screenshot.save(file_path)
183
+ logger.info(f"Screenshot saved to {file_path}")
184
+
185
+ # Create result text
186
+ result_text = "Screenshot captured successfully"
187
+ if file_path:
188
+ result_text += f" and saved to {file_path}"
189
+
190
+ # Record in agent memory if requested
191
+ if read_image and file_path is not None:
192
+ inst = instruction if instruction is not None else ""
193
+ response = self.read_image(
194
+ str(Path(file_path).absolute()), inst
195
+ )
196
+ result_text += f". Agent response: {response}"
197
+
198
+ return result_text
199
+
200
+ except Exception as e:
201
+ logger.error(f"Error taking screenshot: {e}")
202
+ return f"Error taking screenshot: {e}"
203
+
204
+ def get_tools(self) -> List[FunctionTool]:
205
+ r"""Returns a list of FunctionTool objects for screenshot operations.
206
+
207
+ Returns:
208
+ List[FunctionTool]: List of screenshot functions.
209
+ """
210
+ return [
211
+ FunctionTool(self.take_screenshot_and_read_image),
212
+ FunctionTool(self.read_image),
213
+ ]