camel-ai 0.2.75a5__py3-none-any.whl → 0.2.76__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 (103) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +1148 -298
  3. camel/agents/mcp_agent.py +30 -27
  4. camel/configs/__init__.py +9 -0
  5. camel/configs/amd_config.py +70 -0
  6. camel/configs/cometapi_config.py +104 -0
  7. camel/configs/nebius_config.py +103 -0
  8. camel/data_collectors/alpaca_collector.py +15 -6
  9. camel/environments/tic_tac_toe.py +1 -1
  10. camel/interpreters/__init__.py +2 -0
  11. camel/interpreters/docker/Dockerfile +3 -12
  12. camel/interpreters/microsandbox_interpreter.py +395 -0
  13. camel/loaders/__init__.py +11 -2
  14. camel/loaders/chunkr_reader.py +9 -0
  15. camel/memories/__init__.py +2 -1
  16. camel/memories/agent_memories.py +3 -1
  17. camel/memories/blocks/chat_history_block.py +21 -3
  18. camel/memories/records.py +88 -8
  19. camel/messages/base.py +127 -34
  20. camel/models/__init__.py +6 -0
  21. camel/models/amd_model.py +101 -0
  22. camel/models/azure_openai_model.py +0 -6
  23. camel/models/base_model.py +30 -0
  24. camel/models/cometapi_model.py +83 -0
  25. camel/models/model_factory.py +6 -0
  26. camel/models/nebius_model.py +83 -0
  27. camel/models/ollama_model.py +3 -3
  28. camel/models/openai_compatible_model.py +0 -6
  29. camel/models/openai_model.py +0 -6
  30. camel/models/zhipuai_model.py +61 -2
  31. camel/parsers/__init__.py +18 -0
  32. camel/parsers/mcp_tool_call_parser.py +176 -0
  33. camel/retrievers/auto_retriever.py +1 -0
  34. camel/runtimes/daytona_runtime.py +11 -12
  35. camel/societies/workforce/prompts.py +131 -50
  36. camel/societies/workforce/single_agent_worker.py +434 -49
  37. camel/societies/workforce/structured_output_handler.py +30 -18
  38. camel/societies/workforce/task_channel.py +163 -27
  39. camel/societies/workforce/utils.py +105 -12
  40. camel/societies/workforce/workforce.py +1357 -314
  41. camel/societies/workforce/workforce_logger.py +24 -5
  42. camel/storages/key_value_storages/json.py +15 -2
  43. camel/storages/object_storages/google_cloud.py +1 -1
  44. camel/storages/vectordb_storages/oceanbase.py +10 -11
  45. camel/storages/vectordb_storages/tidb.py +8 -6
  46. camel/tasks/task.py +4 -3
  47. camel/toolkits/__init__.py +18 -5
  48. camel/toolkits/aci_toolkit.py +45 -0
  49. camel/toolkits/code_execution.py +28 -1
  50. camel/toolkits/context_summarizer_toolkit.py +684 -0
  51. camel/toolkits/dingtalk.py +1135 -0
  52. camel/toolkits/edgeone_pages_mcp_toolkit.py +11 -31
  53. camel/toolkits/{file_write_toolkit.py → file_toolkit.py} +194 -34
  54. camel/toolkits/function_tool.py +6 -1
  55. camel/toolkits/github_toolkit.py +104 -17
  56. camel/toolkits/google_drive_mcp_toolkit.py +12 -31
  57. camel/toolkits/hybrid_browser_toolkit/config_loader.py +12 -0
  58. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +79 -2
  59. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +95 -59
  60. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  61. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +5 -612
  62. camel/toolkits/hybrid_browser_toolkit/ts/package.json +0 -1
  63. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +619 -95
  64. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +7 -2
  65. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +115 -219
  66. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  67. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  68. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  69. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +1 -0
  70. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +39 -6
  71. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +412 -133
  72. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +9 -5
  73. camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +98 -31
  74. camel/toolkits/markitdown_toolkit.py +27 -1
  75. camel/toolkits/math_toolkit.py +64 -10
  76. camel/toolkits/mcp_toolkit.py +348 -348
  77. camel/toolkits/message_integration.py +3 -0
  78. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  79. camel/toolkits/note_taking_toolkit.py +18 -8
  80. camel/toolkits/notion_mcp_toolkit.py +16 -26
  81. camel/toolkits/origene_mcp_toolkit.py +8 -49
  82. camel/toolkits/playwright_mcp_toolkit.py +12 -31
  83. camel/toolkits/resend_toolkit.py +168 -0
  84. camel/toolkits/search_toolkit.py +13 -2
  85. camel/toolkits/slack_toolkit.py +50 -1
  86. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  87. camel/toolkits/terminal_toolkit/terminal_toolkit.py +924 -0
  88. camel/toolkits/terminal_toolkit/utils.py +532 -0
  89. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  90. camel/toolkits/video_analysis_toolkit.py +17 -11
  91. camel/toolkits/wechat_official_toolkit.py +483 -0
  92. camel/types/enums.py +155 -1
  93. camel/types/unified_model_type.py +10 -0
  94. camel/utils/commons.py +17 -0
  95. camel/utils/context_utils.py +804 -0
  96. camel/utils/mcp.py +136 -2
  97. camel/utils/token_counting.py +25 -17
  98. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76.dist-info}/METADATA +158 -67
  99. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76.dist-info}/RECORD +101 -80
  100. camel/loaders/pandas_reader.py +0 -368
  101. camel/toolkits/terminal_toolkit.py +0 -1788
  102. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76.dist-info}/WHEEL +0 -0
  103. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76.dist-info}/licenses/LICENSE +0 -0
@@ -464,6 +464,9 @@ class ToolkitMessageIntegration:
464
464
  # Apply the new signature to the wrapper
465
465
  wrapper.__signature__ = new_sig # type: ignore[attr-defined]
466
466
 
467
+ # Mark this function as enhanced by message integration
468
+ wrapper.__message_integration_enhanced__ = True # type: ignore[attr-defined]
469
+
467
470
  # Create a hybrid approach:
468
471
  # store toolkit instance info but preserve calling behavior
469
472
  # We'll use a property-like
@@ -0,0 +1,195 @@
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 typing import Any, Dict, List, Optional
17
+
18
+ from camel.toolkits import BaseToolkit, FunctionTool
19
+
20
+ from .mcp_toolkit import MCPToolkit
21
+
22
+
23
+ class MinimaxMCPToolkit(BaseToolkit):
24
+ r"""MinimaxMCPToolkit provides an interface for interacting with
25
+ MiniMax AI services using the MiniMax MCP server.
26
+
27
+ This toolkit enables access to MiniMax's multimedia generation
28
+ capabilities including text-to-audio, voice cloning, video generation,
29
+ image generation, music generation, and voice design.
30
+
31
+ This toolkit can be used as an async context manager for automatic
32
+ connection management:
33
+
34
+ # Using explicit API key
35
+ async with MinimaxMCPToolkit(api_key="your-key") as toolkit:
36
+ tools = toolkit.get_tools()
37
+ # Toolkit is automatically disconnected when exiting
38
+
39
+ # Using environment variables (recommended for security)
40
+ # Set MINIMAX_API_KEY=your-key in environment
41
+ async with MinimaxMCPToolkit() as toolkit:
42
+ tools = toolkit.get_tools()
43
+
44
+ Environment Variables:
45
+ MINIMAX_API_KEY: MiniMax API key for authentication
46
+ MINIMAX_API_HOST: API host URL (default: https://api.minimax.io)
47
+ MINIMAX_MCP_BASE_PATH: Base path for output files
48
+
49
+ Attributes:
50
+ timeout (Optional[float]): Connection timeout in seconds.
51
+ (default: :obj:`None`)
52
+ """
53
+
54
+ def __init__(
55
+ self,
56
+ api_key: Optional[str] = None,
57
+ api_host: str = "https://api.minimax.io",
58
+ base_path: Optional[str] = None,
59
+ timeout: Optional[float] = None,
60
+ ) -> None:
61
+ r"""Initializes the MinimaxMCPToolkit.
62
+
63
+ Args:
64
+ api_key (Optional[str]): MiniMax API key for authentication.
65
+ If None, will attempt to read from MINIMAX_API_KEY
66
+ environment variable. (default: :obj:`None`)
67
+ api_host (str): MiniMax API host URL. Can be either
68
+ "https://api.minimax.io" (global) or
69
+ "https://api.minimaxi.com" (mainland China).
70
+ Can also be read from MINIMAX_API_HOST environment variable.
71
+ (default: :obj:`"https://api.minimax.io"`)
72
+ base_path (Optional[str]): Base path for output files.
73
+ If None, uses current working directory. Can also be read
74
+ from MINIMAX_MCP_BASE_PATH environment variable.
75
+ (default: :obj:`None`)
76
+ timeout (Optional[float]): Connection timeout in seconds.
77
+ (default: :obj:`None`)
78
+ """
79
+ super().__init__(timeout=timeout)
80
+
81
+ # Read API key from parameter or environment variable
82
+ if api_key is None:
83
+ api_key = os.getenv("MINIMAX_API_KEY")
84
+
85
+ if not api_key:
86
+ raise ValueError(
87
+ "api_key must be provided either as a parameter or through "
88
+ "the MINIMAX_API_KEY environment variable"
89
+ )
90
+
91
+ # Read API host from environment variable if not overridden
92
+ env_api_host = os.getenv("MINIMAX_API_HOST")
93
+ if env_api_host:
94
+ api_host = env_api_host
95
+
96
+ # Read base path from environment variable if not provided
97
+ if base_path is None:
98
+ base_path = os.getenv("MINIMAX_MCP_BASE_PATH")
99
+
100
+ # Set up environment variables for the MCP server
101
+ env = {
102
+ "MINIMAX_API_KEY": api_key,
103
+ "MINIMAX_API_HOST": api_host,
104
+ }
105
+
106
+ if base_path:
107
+ env["MINIMAX_MCP_BASE_PATH"] = base_path
108
+
109
+ self._mcp_toolkit = MCPToolkit(
110
+ config_dict={
111
+ "mcpServers": {
112
+ "minimax": {
113
+ "command": "uvx",
114
+ "args": ["minimax-mcp", "-y"],
115
+ "env": env,
116
+ }
117
+ }
118
+ },
119
+ timeout=timeout,
120
+ )
121
+
122
+ async def connect(self):
123
+ r"""Explicitly connect to the MiniMax MCP server."""
124
+ await self._mcp_toolkit.connect()
125
+
126
+ async def disconnect(self):
127
+ r"""Explicitly disconnect from the MiniMax MCP server."""
128
+ await self._mcp_toolkit.disconnect()
129
+
130
+ @property
131
+ def is_connected(self) -> bool:
132
+ r"""Check if the toolkit is connected to the MCP server.
133
+
134
+ Returns:
135
+ bool: True if connected, False otherwise.
136
+ """
137
+ return self._mcp_toolkit.is_connected
138
+
139
+ async def __aenter__(self) -> "MinimaxMCPToolkit":
140
+ r"""Async context manager entry point.
141
+
142
+ Returns:
143
+ MinimaxMCPToolkit: The connected toolkit instance.
144
+
145
+ Example:
146
+ async with MinimaxMCPToolkit(api_key="your-key") as toolkit:
147
+ tools = toolkit.get_tools()
148
+ """
149
+ await self.connect()
150
+ return self
151
+
152
+ async def __aexit__(self, _exc_type, _exc_val, _exc_tb) -> None:
153
+ r"""Async context manager exit point.
154
+
155
+ Automatically disconnects from the MiniMax MCP server.
156
+ """
157
+ await self.disconnect()
158
+
159
+ def get_tools(self) -> List[FunctionTool]:
160
+ r"""Returns a list of tools provided by the MiniMax MCP server.
161
+
162
+ This includes tools for:
163
+ - Text-to-audio conversion
164
+ - Voice cloning
165
+ - Video generation
166
+ - Image generation
167
+ - Music generation
168
+ - Voice design
169
+
170
+ Returns:
171
+ List[FunctionTool]: List of available MiniMax AI tools.
172
+ """
173
+ return self._mcp_toolkit.get_tools()
174
+
175
+ def get_text_tools(self) -> str:
176
+ r"""Returns a string containing the descriptions of the tools.
177
+
178
+ Returns:
179
+ str: A string containing the descriptions of all MiniMax tools.
180
+ """
181
+ return self._mcp_toolkit.get_text_tools()
182
+
183
+ async def call_tool(
184
+ self, tool_name: str, tool_args: Dict[str, Any]
185
+ ) -> Any:
186
+ r"""Call a MiniMax tool by name.
187
+
188
+ Args:
189
+ tool_name (str): Name of the tool to call.
190
+ tool_args (Dict[str, Any]): Arguments to pass to the tool.
191
+
192
+ Returns:
193
+ Any: The result of the tool call.
194
+ """
195
+ return await self._mcp_toolkit.call_tool(tool_name, tool_args)
@@ -138,33 +138,43 @@ class NoteTakingToolkit(BaseToolkit):
138
138
  self.registry.append(note_name)
139
139
  self._save_registry()
140
140
 
141
- def create_note(self, note_name: str, content: str) -> str:
141
+ def create_note(
142
+ self, note_name: str, content: str, overwrite: bool = False
143
+ ) -> str:
142
144
  r"""Creates a new note with a unique name.
143
145
 
144
146
  This function will create a new file for your note.
145
- You must provide a `note_name` that does not already exist. If you want
146
- to add content to an existing note, use the `append_note` function
147
- instead.
147
+ By default, you must provide a `note_name` that does not already exist.
148
+ If you want to add content to an existing note, use the `append_note`
149
+ function instead. If you want to overwrite an existing note, set
150
+ `overwrite=True`.
148
151
 
149
152
  Args:
150
153
  note_name (str): The name for your new note (without the .md
151
- extension). This name must be unique.
154
+ extension). This name must be unique unless overwrite is True.
152
155
  content (str): The initial content to write in the note.
156
+ overwrite (bool): Whether to overwrite an existing note.
157
+ Defaults to False.
153
158
 
154
159
  Returns:
155
160
  str: A message confirming the creation of the note or an error if
156
- the note name is not valid or already exists.
161
+ the note name is not valid or already exists
162
+ (when overwrite=False).
157
163
  """
158
164
  try:
159
165
  note_path = self.working_directory / f"{note_name}.md"
166
+ existed_before = note_path.exists()
160
167
 
161
- if note_path.exists():
168
+ if existed_before and not overwrite:
162
169
  return f"Error: Note '{note_name}.md' already exists."
163
170
 
164
171
  note_path.write_text(content, encoding="utf-8")
165
172
  self._register_note(note_name)
166
173
 
167
- return f"Note '{note_name}.md' successfully created."
174
+ if existed_before and overwrite:
175
+ return f"Note '{note_name}.md' successfully overwritten."
176
+ else:
177
+ return f"Note '{note_name}.md' successfully created."
168
178
  except Exception as e:
169
179
  return f"Error creating note: {e}"
170
180
 
@@ -14,12 +14,12 @@
14
14
 
15
15
  from typing import Any, ClassVar, Dict, List, Optional, Set
16
16
 
17
- from camel.toolkits import BaseToolkit, FunctionTool
17
+ from camel.toolkits import FunctionTool
18
18
 
19
19
  from .mcp_toolkit import MCPToolkit
20
20
 
21
21
 
22
- class NotionMCPToolkit(BaseToolkit):
22
+ class NotionMCPToolkit(MCPToolkit):
23
23
  r"""NotionMCPToolkit provides an interface for interacting with Notion
24
24
  through the Model Context Protocol (MCP).
25
25
 
@@ -75,31 +75,21 @@ class NotionMCPToolkit(BaseToolkit):
75
75
  timeout (Optional[float]): Connection timeout in seconds.
76
76
  (default: :obj:`None`)
77
77
  """
78
- super().__init__(timeout=timeout)
79
-
80
- self._mcp_toolkit = MCPToolkit(
81
- config_dict={
82
- "mcpServers": {
83
- "notionMCP": {
84
- "command": "npx",
85
- "args": [
86
- "-y",
87
- "mcp-remote",
88
- "https://mcp.notion.com/mcp",
89
- ],
90
- }
78
+ config_dict = {
79
+ "mcpServers": {
80
+ "notionMCP": {
81
+ "command": "npx",
82
+ "args": [
83
+ "-y",
84
+ "mcp-remote",
85
+ "https://mcp.notion.com/mcp",
86
+ ],
91
87
  }
92
- },
93
- timeout=timeout,
94
- )
95
-
96
- async def connect(self):
97
- r"""Explicitly connect to the Notion MCP server."""
98
- await self._mcp_toolkit.connect()
88
+ }
89
+ }
99
90
 
100
- async def disconnect(self):
101
- r"""Explicitly disconnect from the Notion MCP server."""
102
- await self._mcp_toolkit.disconnect()
91
+ # Initialize parent MCPToolkit with Notion configuration
92
+ super().__init__(config_dict=config_dict, timeout=timeout)
103
93
 
104
94
  def get_tools(self) -> List[FunctionTool]:
105
95
  r"""Returns a list of tools provided by the NotionMCPToolkit.
@@ -108,7 +98,7 @@ class NotionMCPToolkit(BaseToolkit):
108
98
  List[FunctionTool]: List of available tools.
109
99
  """
110
100
  all_tools = []
111
- for client in self._mcp_toolkit.clients:
101
+ for client in self.clients:
112
102
  try:
113
103
  original_build_schema = client._build_tool_schema
114
104
 
@@ -12,12 +12,12 @@
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
 
15
- from typing import Dict, List, Optional
15
+ from typing import Dict, Optional
16
16
 
17
- from camel.toolkits import BaseToolkit, FunctionTool, MCPToolkit
17
+ from .mcp_toolkit import MCPToolkit
18
18
 
19
19
 
20
- class OrigeneToolkit(BaseToolkit):
20
+ class OrigeneToolkit(MCPToolkit):
21
21
  r"""OrigeneToolkit provides an interface for interacting with
22
22
  Origene MCP server.
23
23
 
@@ -43,55 +43,14 @@ class OrigeneToolkit(BaseToolkit):
43
43
 
44
44
  Args:
45
45
  config_dict (Optional[Dict]): Configuration dictionary for MCP
46
- servers. If None, uses default configuration for chembl_mcp.
47
- (default: :obj:`None`)
46
+ servers. If None, raises ValueError as configuration is
47
+ required. (default: :obj:`None`)
48
48
  timeout (Optional[float]): Connection timeout in seconds.
49
49
  (default: :obj:`None`)
50
50
  """
51
- super().__init__(timeout=timeout)
52
-
53
- # Use default configuration if none provided
51
+ # Validate that config_dict is provided
54
52
  if config_dict is None:
55
53
  raise ValueError("config_dict must be provided")
56
54
 
57
- self._mcp_toolkit = MCPToolkit(
58
- config_dict=config_dict,
59
- timeout=timeout,
60
- )
61
-
62
- async def connect(self):
63
- r"""Explicitly connect to the Origene MCP server."""
64
- await self._mcp_toolkit.connect()
65
-
66
- async def disconnect(self):
67
- r"""Explicitly disconnect from the Origene MCP server."""
68
- await self._mcp_toolkit.disconnect()
69
-
70
- async def __aenter__(self) -> "OrigeneToolkit":
71
- r"""Async context manager entry point.
72
-
73
- Returns:
74
- OrigeneToolkit: The connected toolkit instance.
75
-
76
- Example:
77
- async with OrigeneToolkit(config_dict=config) as toolkit:
78
- tools = toolkit.get_tools()
79
- """
80
- await self.connect()
81
- return self
82
-
83
- async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
84
- r"""Async context manager exit point.
85
-
86
- Automatically disconnects from the Origene MCP server.
87
- """
88
- await self.disconnect()
89
- return None
90
-
91
- def get_tools(self) -> List[FunctionTool]:
92
- r"""Returns a list of tools provided by the Origene MCP server.
93
-
94
- Returns:
95
- List[FunctionTool]: List of available tools.
96
- """
97
- return self._mcp_toolkit.get_tools()
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)
@@ -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
+ ]
@@ -12,6 +12,7 @@
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  import os
15
+ import warnings
15
16
  from typing import Any, Dict, List, Literal, Optional, TypeAlias, Union, cast
16
17
 
17
18
  import requests
@@ -683,7 +684,7 @@ class SearchToolkit(BaseToolkit):
683
684
  responses.append({"error": f"google search failed: {e!s}"})
684
685
  return responses
685
686
 
686
- def tavily_search(
687
+ def search_tavily(
687
688
  self, query: str, number_of_result_pages: int = 10, **kwargs
688
689
  ) -> List[Dict[str, Any]]:
689
690
  r"""Use Tavily Search API to search information for the given query.
@@ -1358,7 +1359,7 @@ class SearchToolkit(BaseToolkit):
1358
1359
  FunctionTool(self.search_linkup),
1359
1360
  FunctionTool(self.search_google),
1360
1361
  FunctionTool(self.search_duckduckgo),
1361
- FunctionTool(self.tavily_search),
1362
+ FunctionTool(self.search_tavily),
1362
1363
  FunctionTool(self.search_brave),
1363
1364
  FunctionTool(self.search_bocha),
1364
1365
  FunctionTool(self.search_baidu),
@@ -1367,3 +1368,13 @@ class SearchToolkit(BaseToolkit):
1367
1368
  FunctionTool(self.search_alibaba_tongxiao),
1368
1369
  FunctionTool(self.search_metaso),
1369
1370
  ]
1371
+
1372
+ # Deprecated method alias for backward compatibility
1373
+ def tavily_search(self, *args, **kwargs):
1374
+ r"""Deprecated: Use search_tavily instead for consistency with other search methods."""
1375
+ warnings.warn(
1376
+ "tavily_search is deprecated. Use search_tavily instead for consistency.",
1377
+ DeprecationWarning,
1378
+ stacklevel=2,
1379
+ )
1380
+ return self.search_tavily(*args, **kwargs)