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.
- camel/__init__.py +1 -1
- camel/agents/chat_agent.py +1148 -298
- camel/agents/mcp_agent.py +30 -27
- camel/configs/__init__.py +9 -0
- camel/configs/amd_config.py +70 -0
- camel/configs/cometapi_config.py +104 -0
- camel/configs/nebius_config.py +103 -0
- camel/data_collectors/alpaca_collector.py +15 -6
- camel/environments/tic_tac_toe.py +1 -1
- camel/interpreters/__init__.py +2 -0
- camel/interpreters/docker/Dockerfile +3 -12
- camel/interpreters/microsandbox_interpreter.py +395 -0
- camel/loaders/__init__.py +11 -2
- camel/loaders/chunkr_reader.py +9 -0
- camel/memories/__init__.py +2 -1
- camel/memories/agent_memories.py +3 -1
- camel/memories/blocks/chat_history_block.py +21 -3
- camel/memories/records.py +88 -8
- camel/messages/base.py +127 -34
- camel/models/__init__.py +6 -0
- camel/models/amd_model.py +101 -0
- camel/models/azure_openai_model.py +0 -6
- camel/models/base_model.py +30 -0
- camel/models/cometapi_model.py +83 -0
- camel/models/model_factory.py +6 -0
- camel/models/nebius_model.py +83 -0
- camel/models/ollama_model.py +3 -3
- camel/models/openai_compatible_model.py +0 -6
- camel/models/openai_model.py +0 -6
- camel/models/zhipuai_model.py +61 -2
- camel/parsers/__init__.py +18 -0
- camel/parsers/mcp_tool_call_parser.py +176 -0
- camel/retrievers/auto_retriever.py +1 -0
- camel/runtimes/daytona_runtime.py +11 -12
- camel/societies/workforce/prompts.py +131 -50
- camel/societies/workforce/single_agent_worker.py +434 -49
- camel/societies/workforce/structured_output_handler.py +30 -18
- camel/societies/workforce/task_channel.py +163 -27
- camel/societies/workforce/utils.py +105 -12
- camel/societies/workforce/workforce.py +1357 -314
- camel/societies/workforce/workforce_logger.py +24 -5
- camel/storages/key_value_storages/json.py +15 -2
- camel/storages/object_storages/google_cloud.py +1 -1
- camel/storages/vectordb_storages/oceanbase.py +10 -11
- camel/storages/vectordb_storages/tidb.py +8 -6
- camel/tasks/task.py +4 -3
- camel/toolkits/__init__.py +18 -5
- camel/toolkits/aci_toolkit.py +45 -0
- camel/toolkits/code_execution.py +28 -1
- camel/toolkits/context_summarizer_toolkit.py +684 -0
- camel/toolkits/dingtalk.py +1135 -0
- camel/toolkits/edgeone_pages_mcp_toolkit.py +11 -31
- camel/toolkits/{file_write_toolkit.py → file_toolkit.py} +194 -34
- camel/toolkits/function_tool.py +6 -1
- camel/toolkits/github_toolkit.py +104 -17
- camel/toolkits/google_drive_mcp_toolkit.py +12 -31
- camel/toolkits/hybrid_browser_toolkit/config_loader.py +12 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +79 -2
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +95 -59
- camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +5 -612
- camel/toolkits/hybrid_browser_toolkit/ts/package.json +0 -1
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +619 -95
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +7 -2
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +115 -219
- camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +1 -0
- camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +39 -6
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +412 -133
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +9 -5
- camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +98 -31
- camel/toolkits/markitdown_toolkit.py +27 -1
- camel/toolkits/math_toolkit.py +64 -10
- camel/toolkits/mcp_toolkit.py +348 -348
- camel/toolkits/message_integration.py +3 -0
- camel/toolkits/minimax_mcp_toolkit.py +195 -0
- camel/toolkits/note_taking_toolkit.py +18 -8
- camel/toolkits/notion_mcp_toolkit.py +16 -26
- camel/toolkits/origene_mcp_toolkit.py +8 -49
- camel/toolkits/playwright_mcp_toolkit.py +12 -31
- camel/toolkits/resend_toolkit.py +168 -0
- camel/toolkits/search_toolkit.py +13 -2
- camel/toolkits/slack_toolkit.py +50 -1
- camel/toolkits/terminal_toolkit/__init__.py +18 -0
- camel/toolkits/terminal_toolkit/terminal_toolkit.py +924 -0
- camel/toolkits/terminal_toolkit/utils.py +532 -0
- camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
- camel/toolkits/video_analysis_toolkit.py +17 -11
- camel/toolkits/wechat_official_toolkit.py +483 -0
- camel/types/enums.py +155 -1
- camel/types/unified_model_type.py +10 -0
- camel/utils/commons.py +17 -0
- camel/utils/context_utils.py +804 -0
- camel/utils/mcp.py +136 -2
- camel/utils/token_counting.py +25 -17
- {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76.dist-info}/METADATA +158 -67
- {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76.dist-info}/RECORD +101 -80
- camel/loaders/pandas_reader.py +0 -368
- camel/toolkits/terminal_toolkit.py +0 -1788
- {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76.dist-info}/WHEEL +0 -0
- {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
|
|
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(
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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
|
|
30
|
-
r"""A toolkit for
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
130
|
-
r"""
|
|
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
|
|
136
|
+
file_path (Path): The file path to backup.
|
|
134
137
|
|
|
135
138
|
Returns:
|
|
136
|
-
Path:
|
|
139
|
+
Optional[Path]: Path to the backup file if created, None otherwise.
|
|
137
140
|
"""
|
|
138
|
-
if not file_path.exists():
|
|
139
|
-
return
|
|
141
|
+
if not self.backup_enabled or not file_path.exists():
|
|
142
|
+
return None
|
|
140
143
|
|
|
141
|
-
# Generate
|
|
144
|
+
# Generate backup filename with .bak extension and timestamp
|
|
142
145
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
#
|
|
1010
|
-
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)
|
camel/toolkits/function_tool.py
CHANGED
|
@@ -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
|
-
|
|
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 ""
|
camel/toolkits/github_toolkit.py
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
347
|
-
FunctionTool(self.
|
|
348
|
-
FunctionTool(self.
|
|
349
|
-
FunctionTool(self.
|
|
350
|
-
FunctionTool(self.
|
|
351
|
-
FunctionTool(self.
|
|
352
|
-
FunctionTool(self.
|
|
353
|
-
FunctionTool(self.
|
|
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
|
|
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(
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
68
|
-
|
|
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]]:
|