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
@@ -12,14 +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 List, Optional
16
-
17
- from camel.toolkits import BaseToolkit, FunctionTool
15
+ from typing import Optional
18
16
 
19
17
  from .mcp_toolkit import MCPToolkit
20
18
 
21
19
 
22
- class EdgeOnePagesMCPToolkit(BaseToolkit):
20
+ class EdgeOnePagesMCPToolkit(MCPToolkit):
23
21
  r"""EdgeOnePagesMCPToolkit provides an interface for interacting with
24
22
  EdgeOne pages using the EdgeOne Pages MCP server.
25
23
 
@@ -38,32 +36,14 @@ class EdgeOnePagesMCPToolkit(BaseToolkit):
38
36
  timeout (Optional[float]): Connection timeout in seconds.
39
37
  (default: :obj:`None`)
40
38
  """
41
- super().__init__(timeout=timeout)
42
-
43
- self._mcp_toolkit = MCPToolkit(
44
- config_dict={
45
- "mcpServers": {
46
- "edgeone-pages-mcp-server": {
47
- "command": "npx",
48
- "args": ["edgeone-pages-mcp"],
49
- }
39
+ config_dict = {
40
+ "mcpServers": {
41
+ "edgeone-pages-mcp-server": {
42
+ "command": "npx",
43
+ "args": ["edgeone-pages-mcp"],
50
44
  }
51
- },
52
- timeout=timeout,
53
- )
54
-
55
- async def connect(self):
56
- r"""Explicitly connect to the EdgeOne Pages MCP server."""
57
- await self._mcp_toolkit.connect()
45
+ }
46
+ }
58
47
 
59
- async def disconnect(self):
60
- r"""Explicitly disconnect from the EdgeOne Pages MCP server."""
61
- await self._mcp_toolkit.disconnect()
62
-
63
- def get_tools(self) -> List[FunctionTool]:
64
- r"""Returns a list of tools provided by the EdgeOnePagesMCPToolkit.
65
-
66
- Returns:
67
- List[FunctionTool]: List of available tools.
68
- """
69
- return self._mcp_toolkit.get_tools()
48
+ # Initialize parent MCPToolkit with EdgeOne Pages configuration
49
+ super().__init__(config_dict=config_dict, timeout=timeout)
@@ -15,7 +15,7 @@ import os
15
15
  import re
16
16
  from datetime import datetime
17
17
  from pathlib import Path
18
- from typing import List, Optional, Tuple, Union
18
+ from typing import Dict, List, Optional, Tuple, Union
19
19
 
20
20
  from camel.logger import get_logger
21
21
  from camel.toolkits.base import BaseToolkit
@@ -26,14 +26,17 @@ logger = get_logger(__name__)
26
26
 
27
27
 
28
28
  @MCPServer()
29
- class FileWriteToolkit(BaseToolkit):
30
- r"""A toolkit for creating, writing, and modifying text in files.
31
-
32
- This class provides cross-platform (macOS, Linux, Windows) support for
33
- writing to various file formats (Markdown, DOCX, PDF, and plaintext),
34
- replacing text in existing files, automatic filename uniquification to
35
- prevent overwrites, custom encoding and enhanced formatting options for
36
- specialized formats.
29
+ class FileToolkit(BaseToolkit):
30
+ r"""A comprehensive toolkit for file operations including reading,
31
+ writing, and editing files.
32
+
33
+ This class provides cross-platform (macOS, Linux, Windows) support for:
34
+ - Reading various file formats (text, JSON, YAML, PDF, DOCX)
35
+ - Writing to multiple formats (Markdown, DOCX, PDF, plaintext, JSON,
36
+ YAML, CSV, HTML)
37
+ - Editing and modifying existing files with content replacement
38
+ - Automatic backup creation before modifications
39
+ - Custom encoding and enhanced formatting options
37
40
  """
38
41
 
39
42
  def __init__(
@@ -126,36 +129,32 @@ class FileWriteToolkit(BaseToolkit):
126
129
  with file_path.open("w", encoding=encoding) as f:
127
130
  f.write(content)
128
131
 
129
- def _generate_unique_filename(self, file_path: Path) -> Path:
130
- r"""Generate a unique filename if the target file already exists.
132
+ def _create_backup(self, file_path: Path) -> Optional[Path]:
133
+ r"""Create a backup of the file if it exists and backup is enabled.
131
134
 
132
135
  Args:
133
- file_path (Path): The original file path.
136
+ file_path (Path): The file path to backup.
134
137
 
135
138
  Returns:
136
- Path: A unique file path that doesn't exist yet.
139
+ Optional[Path]: Path to the backup file if created, None otherwise.
137
140
  """
138
- if not file_path.exists():
139
- return file_path
141
+ if not self.backup_enabled or not file_path.exists():
142
+ return None
140
143
 
141
- # Generate unique filename with timestamp and counter
144
+ # Generate backup filename with .bak extension and timestamp
142
145
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
143
- stem = file_path.stem
144
- suffix = file_path.suffix
145
- parent = file_path.parent
146
-
147
- # First try with timestamp
148
- new_path = parent / f"{stem}_{timestamp}{suffix}"
149
- if not new_path.exists():
150
- return new_path
151
-
152
- # If timestamp version exists, add counter
153
- counter = 1
154
- while True:
155
- new_path = parent / f"{stem}_{timestamp}_{counter}{suffix}"
156
- if not new_path.exists():
157
- return new_path
158
- counter += 1
146
+ backup_path = file_path.parent / f"{file_path.name}.{timestamp}.bak"
147
+
148
+ # Copy the file to backup location
149
+ import shutil
150
+
151
+ try:
152
+ shutil.copy2(file_path, backup_path)
153
+ logger.info(f"Created backup: {backup_path}")
154
+ return backup_path
155
+ except Exception as e:
156
+ logger.warning(f"Failed to create backup: {e}")
157
+ return None
159
158
 
160
159
  def _write_docx_file(self, file_path: Path, content: str) -> None:
161
160
  r"""Write text content to a DOCX file with default formatting.
@@ -1006,8 +1005,9 @@ class FileWriteToolkit(BaseToolkit):
1006
1005
  file_path = self._resolve_filepath(filename)
1007
1006
  file_path.parent.mkdir(parents=True, exist_ok=True)
1008
1007
 
1009
- # Generate unique filename if file exists
1010
- file_path = self._generate_unique_filename(file_path)
1008
+ # Create backup of existing file if backup is enabled
1009
+ if file_path.exists() and self.backup_enabled:
1010
+ self._create_backup(file_path)
1011
1011
 
1012
1012
  extension = file_path.suffix.lower()
1013
1013
 
@@ -1062,6 +1062,144 @@ class FileWriteToolkit(BaseToolkit):
1062
1062
  logger.error(error_msg)
1063
1063
  return error_msg
1064
1064
 
1065
+ # ----------------------------------------------
1066
+ # Read File Functions
1067
+ # ----------------------------------------------
1068
+ def read_file(
1069
+ self, file_paths: Union[str, List[str]]
1070
+ ) -> Union[str, Dict[str, str]]:
1071
+ r"""Read and return content of one or more files using MarkItDown
1072
+ for better format support.
1073
+
1074
+ This method uses MarkItDownLoader to convert various file formats
1075
+ to Markdown. It supports a wide range of formats including:
1076
+ - PDF (.pdf)
1077
+ - Microsoft Office: Word (.doc, .docx), Excel (.xls, .xlsx),
1078
+ PowerPoint (.ppt, .pptx)
1079
+ - EPUB (.epub)
1080
+ - HTML (.html, .htm)
1081
+ - Images (.jpg, .jpeg, .png) for OCR
1082
+ - Audio (.mp3, .wav) for transcription
1083
+ - Text-based formats (.csv, .json, .xml, .txt, .md)
1084
+ - ZIP archives (.zip)
1085
+
1086
+ Args:
1087
+ file_paths (Union[str, List[str]]): A single file path or a list
1088
+ of file paths to read. Paths can be relative or absolute.
1089
+ If relative, they will be resolved relative to the working
1090
+ directory.
1091
+
1092
+ Returns:
1093
+ Union[str, Dict[str, str]]:
1094
+ - If a single file path is provided: Returns the content as
1095
+ a string.
1096
+ - If multiple file paths are provided: Returns a dictionary
1097
+ where keys are file paths and values are the corresponding
1098
+ content in Markdown format.
1099
+ If conversion fails, returns an error message.
1100
+ """
1101
+ from camel.loaders.markitdown import MarkItDownLoader
1102
+
1103
+ try:
1104
+ # Handle single file path for backward compatibility
1105
+ if isinstance(file_paths, str):
1106
+ resolved_path = self._resolve_filepath(file_paths)
1107
+
1108
+ # Use MarkItDownLoader to convert the file
1109
+ result = MarkItDownLoader().convert_files(
1110
+ file_paths=[str(resolved_path)], parallel=False
1111
+ )
1112
+
1113
+ # Return the converted content or error message
1114
+ return result.get(
1115
+ str(resolved_path), f"Failed to read file: {resolved_path}"
1116
+ )
1117
+
1118
+ # Handle multiple file paths
1119
+ else:
1120
+ resolved_paths = [
1121
+ str(self._resolve_filepath(fp)) for fp in file_paths
1122
+ ]
1123
+
1124
+ # Use MarkItDownLoader to convert files in parallel
1125
+ result = MarkItDownLoader().convert_files(
1126
+ file_paths=resolved_paths, parallel=True
1127
+ )
1128
+
1129
+ # Map back to original paths if needed
1130
+ return_dict = {}
1131
+ for original, resolved in zip(file_paths, resolved_paths):
1132
+ return_dict[original] = result.get(
1133
+ resolved, f"Failed to read file: {resolved}"
1134
+ )
1135
+
1136
+ return return_dict
1137
+
1138
+ except Exception as e:
1139
+ return f"Error reading file(s): {e}"
1140
+
1141
+ # ----------------------------------------------
1142
+ # Edit File Functions
1143
+ # ----------------------------------------------
1144
+ def edit_file(
1145
+ self, file_path: str, old_content: str, new_content: str
1146
+ ) -> str:
1147
+ r"""Edit a file by replacing specified content.
1148
+
1149
+ This method performs simple text replacement in files. It reads
1150
+ the file, replaces all occurrences of old_content with new_content,
1151
+ and writes the result back.
1152
+
1153
+ Args:
1154
+ file_path (str): The path to the file to edit. Can be
1155
+ relative or absolute. If relative, it will be resolved
1156
+ relative to the working directory.
1157
+ old_content (str): The exact text to find and replace.
1158
+ new_content (str): The text to replace old_content with.
1159
+
1160
+ Returns:
1161
+ str: A success message if the edit was successful, or an
1162
+ error message if the content wasn't found or an error occurred.
1163
+ """
1164
+ try:
1165
+ working_path = self._resolve_filepath(file_path)
1166
+
1167
+ if not working_path.exists():
1168
+ return f"Error: File {working_path} does not exist"
1169
+
1170
+ # Create backup before editing if enabled
1171
+ self._create_backup(working_path)
1172
+
1173
+ # Read the file content
1174
+ try:
1175
+ file_text = working_path.read_text(
1176
+ encoding=self.default_encoding
1177
+ )
1178
+ except Exception as e:
1179
+ return f"Error reading file: {e}"
1180
+
1181
+ # Check if the old_content exists in the file
1182
+ if old_content not in file_text:
1183
+ return (
1184
+ f"No replacement performed: '{old_content}' not found in "
1185
+ f"{working_path}."
1186
+ )
1187
+
1188
+ # Replace the content
1189
+ new_file_text = file_text.replace(old_content, new_content)
1190
+
1191
+ # Write back to file
1192
+ try:
1193
+ working_path.write_text(
1194
+ new_file_text, encoding=self.default_encoding
1195
+ )
1196
+ return f"Successfully edited {working_path}"
1197
+ except Exception as e:
1198
+ return f"Error writing file: {e}"
1199
+
1200
+ except Exception as e:
1201
+ return f"Error editing file: {e}"
1202
+
1065
1203
  def get_tools(self) -> List[FunctionTool]:
1066
1204
  r"""Return a list of FunctionTool objects representing the functions
1067
1205
  in the toolkit.
@@ -1072,4 +1210,26 @@ class FileWriteToolkit(BaseToolkit):
1072
1210
  """
1073
1211
  return [
1074
1212
  FunctionTool(self.write_to_file),
1213
+ FunctionTool(self.read_file),
1214
+ FunctionTool(self.edit_file),
1075
1215
  ]
1216
+
1217
+
1218
+ # Backward compatibility: FileWriteToolkit as deprecated alias
1219
+ class FileWriteToolkit(FileToolkit):
1220
+ r"""Deprecated: Use FileToolkit instead.
1221
+
1222
+ This class is maintained for backward compatibility only.
1223
+ Please use FileToolkit for new code.
1224
+ """
1225
+
1226
+ def __init__(self, *args, **kwargs):
1227
+ import warnings
1228
+
1229
+ warnings.warn(
1230
+ "FileWriteToolkit is deprecated and will be removed in a "
1231
+ "future version. Please use FileToolkit instead.",
1232
+ DeprecationWarning,
1233
+ stacklevel=2,
1234
+ )
1235
+ super().__init__(*args, **kwargs)
@@ -156,7 +156,12 @@ def get_openai_tool_schema(func: Callable) -> Dict[str, Any]:
156
156
  if (name := param.arg_name) in parameters_dict["properties"] and (
157
157
  description := param.description
158
158
  ):
159
- parameters_dict["properties"][name]["description"] = description
159
+ # OpenAI does not allow descriptions on properties that use $ref.
160
+ # To avoid schema errors, we only add the description if "$ref" is
161
+ # not present.
162
+ prop = parameters_dict["properties"][name]
163
+ if "$ref" not in prop:
164
+ prop["description"] = description
160
165
 
161
166
  short_description = docstring.short_description or ""
162
167
  long_description = docstring.long_description or ""
@@ -14,6 +14,7 @@
14
14
 
15
15
  import logging
16
16
  import os
17
+ import warnings
17
18
  from typing import Dict, List, Literal, Optional, Union
18
19
 
19
20
  from camel.toolkits import FunctionTool
@@ -80,7 +81,7 @@ class GithubToolkit(BaseToolkit):
80
81
  )
81
82
  return GITHUB_ACCESS_TOKEN
82
83
 
83
- def create_pull_request(
84
+ def github_create_pull_request(
84
85
  self,
85
86
  repo_name: str,
86
87
  file_path: str,
@@ -150,7 +151,7 @@ class GithubToolkit(BaseToolkit):
150
151
  else:
151
152
  raise ValueError("PRs with multiple files aren't supported yet.")
152
153
 
153
- def get_issue_list(
154
+ def github_get_issue_list(
154
155
  self, repo_name: str, state: Literal["open", "closed", "all"] = "all"
155
156
  ) -> List[Dict[str, object]]:
156
157
  r"""Retrieves all issues from the GitHub repository.
@@ -177,7 +178,9 @@ class GithubToolkit(BaseToolkit):
177
178
 
178
179
  return issues_info
179
180
 
180
- def get_issue_content(self, repo_name: str, issue_number: int) -> str:
181
+ def github_get_issue_content(
182
+ self, repo_name: str, issue_number: int
183
+ ) -> str:
181
184
  r"""Retrieves the content of a specific issue by its number.
182
185
 
183
186
  Args:
@@ -194,7 +197,7 @@ class GithubToolkit(BaseToolkit):
194
197
  except Exception as e:
195
198
  return f"can't get Issue number {issue_number}: {e!s}"
196
199
 
197
- def get_pull_request_list(
200
+ def github_get_pull_request_list(
198
201
  self, repo_name: str, state: Literal["open", "closed", "all"] = "all"
199
202
  ) -> List[Dict[str, object]]:
200
203
  r"""Retrieves all pull requests from the GitHub repository.
@@ -221,7 +224,7 @@ class GithubToolkit(BaseToolkit):
221
224
 
222
225
  return pull_requests_info
223
226
 
224
- def get_pull_request_code(
227
+ def github_get_pull_request_code(
225
228
  self, repo_name: str, pr_number: int
226
229
  ) -> List[Dict[str, str]]:
227
230
  r"""Retrieves the code changes of a specific pull request.
@@ -253,7 +256,7 @@ class GithubToolkit(BaseToolkit):
253
256
 
254
257
  return files_changed
255
258
 
256
- def get_pull_request_comments(
259
+ def github_get_pull_request_comments(
257
260
  self, repo_name: str, pr_number: int
258
261
  ) -> List[Dict[str, str]]:
259
262
  r"""Retrieves the comments from a specific pull request.
@@ -278,7 +281,9 @@ class GithubToolkit(BaseToolkit):
278
281
 
279
282
  return comments
280
283
 
281
- def get_all_file_paths(self, repo_name: str, path: str = "") -> List[str]:
284
+ def github_get_all_file_paths(
285
+ self, repo_name: str, path: str = ""
286
+ ) -> List[str]:
282
287
  r"""Recursively retrieves all file paths in the GitHub repository.
283
288
 
284
289
  Args:
@@ -308,13 +313,15 @@ class GithubToolkit(BaseToolkit):
308
313
  for content in contents:
309
314
  if content.type == "dir":
310
315
  # If it's a directory, recursively retrieve its file paths
311
- files.extend(self.get_all_file_paths(content.path))
316
+ files.extend(self.github_get_all_file_paths(content.path))
312
317
  else:
313
318
  # If it's a file, add its path to the list
314
319
  files.append(content.path)
315
320
  return files
316
321
 
317
- def retrieve_file_content(self, repo_name: str, file_path: str) -> str:
322
+ def github_retrieve_file_content(
323
+ self, repo_name: str, file_path: str
324
+ ) -> str:
318
325
  r"""Retrieves the content of a file from the GitHub repository.
319
326
 
320
327
  Args:
@@ -343,12 +350,92 @@ class GithubToolkit(BaseToolkit):
343
350
  the functions in the toolkit.
344
351
  """
345
352
  return [
346
- FunctionTool(self.create_pull_request),
347
- FunctionTool(self.get_issue_list),
348
- FunctionTool(self.get_issue_content),
349
- FunctionTool(self.get_pull_request_list),
350
- FunctionTool(self.get_pull_request_code),
351
- FunctionTool(self.get_pull_request_comments),
352
- FunctionTool(self.get_all_file_paths),
353
- FunctionTool(self.retrieve_file_content),
353
+ FunctionTool(self.github_create_pull_request),
354
+ FunctionTool(self.github_get_issue_list),
355
+ FunctionTool(self.github_get_issue_content),
356
+ FunctionTool(self.github_get_pull_request_list),
357
+ FunctionTool(self.github_get_pull_request_code),
358
+ FunctionTool(self.github_get_pull_request_comments),
359
+ FunctionTool(self.github_get_all_file_paths),
360
+ FunctionTool(self.github_retrieve_file_content),
354
361
  ]
362
+
363
+ # Deprecated method aliases for backward compatibility
364
+ def create_pull_request(self, *args, **kwargs):
365
+ r"""Deprecated: Use github_create_pull_request instead."""
366
+ warnings.warn(
367
+ "create_pull_request is deprecated. Use "
368
+ "github_create_pull_request instead.",
369
+ DeprecationWarning,
370
+ stacklevel=2,
371
+ )
372
+ return self.github_create_pull_request(*args, **kwargs)
373
+
374
+ def get_issue_list(self, *args, **kwargs):
375
+ r"""Deprecated: Use github_get_issue_list instead."""
376
+ warnings.warn(
377
+ "get_issue_list is deprecated. Use github_get_issue_list instead.",
378
+ DeprecationWarning,
379
+ stacklevel=2,
380
+ )
381
+ return self.github_get_issue_list(*args, **kwargs)
382
+
383
+ def get_issue_content(self, *args, **kwargs):
384
+ r"""Deprecated: Use github_get_issue_content instead."""
385
+ warnings.warn(
386
+ "get_issue_content is deprecated. Use "
387
+ "github_get_issue_content instead.",
388
+ DeprecationWarning,
389
+ stacklevel=2,
390
+ )
391
+ return self.github_get_issue_content(*args, **kwargs)
392
+
393
+ def get_pull_request_list(self, *args, **kwargs):
394
+ r"""Deprecated: Use github_get_pull_request_list instead."""
395
+ warnings.warn(
396
+ "get_pull_request_list is deprecated. "
397
+ "Use github_get_pull_request_list instead.",
398
+ DeprecationWarning,
399
+ stacklevel=2,
400
+ )
401
+ return self.github_get_pull_request_list(*args, **kwargs)
402
+
403
+ def get_pull_request_code(self, *args, **kwargs):
404
+ r"""Deprecated: Use github_get_pull_request_code instead."""
405
+ warnings.warn(
406
+ "get_pull_request_code is deprecated. Use "
407
+ "github_get_pull_request_code instead.",
408
+ DeprecationWarning,
409
+ stacklevel=2,
410
+ )
411
+ return self.github_get_pull_request_code(*args, **kwargs)
412
+
413
+ def get_pull_request_comments(self, *args, **kwargs):
414
+ r"""Deprecated: Use github_get_pull_request_comments instead."""
415
+ warnings.warn(
416
+ "get_pull_request_comments is deprecated. "
417
+ "Use github_get_pull_request_comments instead.",
418
+ DeprecationWarning,
419
+ stacklevel=2,
420
+ )
421
+ return self.github_get_pull_request_comments(*args, **kwargs)
422
+
423
+ def get_all_file_paths(self, *args, **kwargs):
424
+ r"""Deprecated: Use github_get_all_file_paths instead."""
425
+ warnings.warn(
426
+ "get_all_file_paths is deprecated. Use "
427
+ "github_get_all_file_paths instead.",
428
+ DeprecationWarning,
429
+ stacklevel=2,
430
+ )
431
+ return self.github_get_all_file_paths(*args, **kwargs)
432
+
433
+ def retrieve_file_content(self, *args, **kwargs):
434
+ r"""Deprecated: Use github_retrieve_file_content instead."""
435
+ warnings.warn(
436
+ "retrieve_file_content is deprecated. "
437
+ "Use github_retrieve_file_content instead.",
438
+ DeprecationWarning,
439
+ stacklevel=2,
440
+ )
441
+ return self.github_retrieve_file_content(*args, **kwargs)
@@ -12,14 +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 List, Optional
16
-
17
- from camel.toolkits import BaseToolkit, FunctionTool
15
+ from typing import Optional
18
16
 
19
17
  from .mcp_toolkit import MCPToolkit
20
18
 
21
19
 
22
- class GoogleDriveMCPToolkit(BaseToolkit):
20
+ class GoogleDriveMCPToolkit(MCPToolkit):
23
21
  r"""GoogleDriveMCPToolkit provides an interface for interacting with
24
22
  Google Drive using the Google Drive MCP server.
25
23
 
@@ -41,33 +39,16 @@ class GoogleDriveMCPToolkit(BaseToolkit):
41
39
  credentials_path (Optional[str]): Path to the Google Drive
42
40
  credentials file. (default: :obj:`None`)
43
41
  """
44
- super().__init__(timeout=timeout)
45
42
 
46
- self._mcp_toolkit = MCPToolkit(
47
- config_dict={
48
- "mcpServers": {
49
- "gdrive": {
50
- "command": "npx",
51
- "args": ["-y", "@modelcontextprotocol/server-gdrive"],
52
- "env": {"GDRIVE_CREDENTIALS_PATH": credentials_path},
53
- }
43
+ config_dict = {
44
+ "mcpServers": {
45
+ "gdrive": {
46
+ "command": "npx",
47
+ "args": ["-y", "@modelcontextprotocol/server-gdrive"],
48
+ "env": {"GDRIVE_CREDENTIALS_PATH": credentials_path},
54
49
  }
55
- },
56
- timeout=timeout,
57
- )
58
-
59
- async def connect(self):
60
- r"""Explicitly connect to the Google Drive MCP server."""
61
- await self._mcp_toolkit.connect()
62
-
63
- async def disconnect(self):
64
- r"""Explicitly disconnect from the Google Drive MCP server."""
65
- await self._mcp_toolkit.disconnect()
50
+ }
51
+ }
66
52
 
67
- def get_tools(self) -> List[FunctionTool]:
68
- r"""Returns a list of tools provided by the GoogleDriveMCPToolkit.
69
-
70
- Returns:
71
- List[FunctionTool]: List of available tools.
72
- """
73
- return self._mcp_toolkit.get_tools()
53
+ # Initialize parent MCPToolkit with Playwright configuration
54
+ super().__init__(config_dict=config_dict, timeout=timeout)
@@ -43,6 +43,10 @@ class BrowserConfig:
43
43
  # CDP connection configuration
44
44
  connect_over_cdp: bool = False
45
45
  cdp_url: Optional[str] = None
46
+ cdp_keep_current_page: bool = False
47
+
48
+ # Full visual mode configuration
49
+ full_visual_mode: bool = False
46
50
 
47
51
 
48
52
  @dataclass
@@ -51,6 +55,7 @@ class ToolkitConfig:
51
55
 
52
56
  cache_dir: str = "tmp/"
53
57
  browser_log_to_file: bool = False
58
+ log_dir: Optional[str] = None
54
59
  session_id: Optional[str] = None
55
60
  enabled_tools: Optional[list] = None
56
61
 
@@ -106,6 +111,8 @@ class ConfigLoader:
106
111
  browser_kwargs["connect_over_cdp"] = value
107
112
  elif key == "cdpUrl":
108
113
  browser_kwargs["cdp_url"] = value
114
+ elif key == "cdpKeepCurrentPage":
115
+ browser_kwargs["cdp_keep_current_page"] = value
109
116
  elif key == "consoleLogLimit":
110
117
  browser_kwargs["console_log_limit"] = value
111
118
  elif key == "cacheDir":
@@ -116,6 +123,8 @@ class ConfigLoader:
116
123
  toolkit_kwargs["session_id"] = value
117
124
  elif key == "enabledTools":
118
125
  toolkit_kwargs["enabled_tools"] = value
126
+ elif key == "fullVisualMode":
127
+ browser_kwargs["full_visual_mode"] = value
119
128
 
120
129
  browser_config = BrowserConfig(**browser_kwargs)
121
130
  toolkit_config = ToolkitConfig(**toolkit_kwargs)
@@ -142,10 +151,13 @@ class ConfigLoader:
142
151
  "screenshotTimeout": self.browser_config.screenshot_timeout,
143
152
  "pageStabilityTimeout": self.browser_config.page_stability_timeout,
144
153
  "browser_log_to_file": self.toolkit_config.browser_log_to_file,
154
+ "log_dir": self.toolkit_config.log_dir,
145
155
  "session_id": self.toolkit_config.session_id,
146
156
  "viewport_limit": self.browser_config.viewport_limit,
147
157
  "connectOverCdp": self.browser_config.connect_over_cdp,
148
158
  "cdpUrl": self.browser_config.cdp_url,
159
+ "cdpKeepCurrentPage": self.browser_config.cdp_keep_current_page,
160
+ "fullVisualMode": self.browser_config.full_visual_mode,
149
161
  }
150
162
 
151
163
  def get_timeout_config(self) -> Dict[str, Optional[int]]: