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.
- camel/__init__.py +1 -1
- camel/agents/_types.py +6 -2
- camel/agents/_utils.py +38 -0
- camel/agents/chat_agent.py +4014 -410
- camel/agents/mcp_agent.py +30 -27
- camel/agents/repo_agent.py +2 -1
- camel/benchmarks/browsecomp.py +6 -6
- camel/configs/__init__.py +15 -0
- camel/configs/aihubmix_config.py +88 -0
- camel/configs/amd_config.py +70 -0
- camel/configs/cometapi_config.py +104 -0
- camel/configs/minimax_config.py +93 -0
- camel/configs/nebius_config.py +103 -0
- camel/configs/vllm_config.py +2 -0
- camel/data_collectors/alpaca_collector.py +15 -6
- camel/datagen/self_improving_cot.py +1 -1
- camel/datasets/base_generator.py +39 -10
- camel/environments/__init__.py +12 -0
- camel/environments/rlcards_env.py +860 -0
- camel/environments/single_step.py +28 -3
- camel/environments/tic_tac_toe.py +1 -1
- camel/interpreters/__init__.py +2 -0
- camel/interpreters/docker/Dockerfile +4 -16
- camel/interpreters/docker_interpreter.py +3 -2
- camel/interpreters/e2b_interpreter.py +34 -1
- camel/interpreters/internal_python_interpreter.py +51 -2
- camel/interpreters/microsandbox_interpreter.py +395 -0
- camel/loaders/__init__.py +11 -2
- camel/loaders/base_loader.py +85 -0
- camel/loaders/chunkr_reader.py +9 -0
- camel/loaders/firecrawl_reader.py +4 -4
- camel/logger.py +1 -1
- camel/memories/agent_memories.py +84 -1
- camel/memories/base.py +34 -0
- camel/memories/blocks/chat_history_block.py +122 -4
- camel/memories/blocks/vectordb_block.py +8 -1
- camel/memories/context_creators/score_based.py +29 -237
- camel/memories/records.py +88 -8
- camel/messages/base.py +166 -40
- camel/messages/func_message.py +32 -5
- camel/models/__init__.py +10 -0
- camel/models/aihubmix_model.py +83 -0
- camel/models/aiml_model.py +1 -16
- camel/models/amd_model.py +101 -0
- camel/models/anthropic_model.py +117 -18
- camel/models/aws_bedrock_model.py +2 -33
- camel/models/azure_openai_model.py +205 -91
- camel/models/base_audio_model.py +3 -1
- camel/models/base_model.py +189 -24
- camel/models/cohere_model.py +5 -17
- camel/models/cometapi_model.py +83 -0
- camel/models/crynux_model.py +1 -16
- camel/models/deepseek_model.py +6 -16
- camel/models/fish_audio_model.py +6 -0
- camel/models/gemini_model.py +71 -20
- camel/models/groq_model.py +1 -17
- camel/models/internlm_model.py +1 -16
- camel/models/litellm_model.py +49 -32
- camel/models/lmstudio_model.py +1 -17
- camel/models/minimax_model.py +83 -0
- camel/models/mistral_model.py +1 -16
- camel/models/model_factory.py +27 -1
- camel/models/model_manager.py +24 -6
- camel/models/modelscope_model.py +1 -16
- camel/models/moonshot_model.py +185 -19
- camel/models/nebius_model.py +83 -0
- camel/models/nemotron_model.py +0 -5
- camel/models/netmind_model.py +1 -16
- camel/models/novita_model.py +1 -16
- camel/models/nvidia_model.py +1 -16
- camel/models/ollama_model.py +4 -19
- camel/models/openai_compatible_model.py +171 -46
- camel/models/openai_model.py +205 -77
- camel/models/openrouter_model.py +1 -17
- camel/models/ppio_model.py +1 -16
- camel/models/qianfan_model.py +1 -16
- camel/models/qwen_model.py +1 -16
- camel/models/reka_model.py +1 -16
- camel/models/samba_model.py +34 -47
- camel/models/sglang_model.py +64 -31
- camel/models/siliconflow_model.py +1 -16
- camel/models/stub_model.py +0 -4
- camel/models/togetherai_model.py +1 -16
- camel/models/vllm_model.py +1 -16
- camel/models/volcano_model.py +0 -17
- camel/models/watsonx_model.py +1 -16
- camel/models/yi_model.py +1 -16
- camel/models/zhipuai_model.py +60 -16
- camel/parsers/__init__.py +18 -0
- camel/parsers/mcp_tool_call_parser.py +176 -0
- camel/retrievers/auto_retriever.py +1 -0
- camel/runtimes/configs.py +11 -11
- camel/runtimes/daytona_runtime.py +15 -16
- camel/runtimes/docker_runtime.py +6 -6
- camel/runtimes/remote_http_runtime.py +5 -5
- camel/services/agent_openapi_server.py +380 -0
- camel/societies/__init__.py +2 -0
- camel/societies/role_playing.py +26 -28
- camel/societies/workforce/__init__.py +2 -0
- camel/societies/workforce/events.py +122 -0
- camel/societies/workforce/prompts.py +249 -38
- camel/societies/workforce/role_playing_worker.py +82 -20
- camel/societies/workforce/single_agent_worker.py +634 -34
- camel/societies/workforce/structured_output_handler.py +512 -0
- camel/societies/workforce/task_channel.py +169 -23
- camel/societies/workforce/utils.py +176 -9
- camel/societies/workforce/worker.py +77 -23
- camel/societies/workforce/workflow_memory_manager.py +772 -0
- camel/societies/workforce/workforce.py +3168 -478
- camel/societies/workforce/workforce_callback.py +74 -0
- camel/societies/workforce/workforce_logger.py +203 -175
- camel/societies/workforce/workforce_metrics.py +33 -0
- camel/storages/__init__.py +4 -0
- camel/storages/key_value_storages/json.py +15 -2
- camel/storages/key_value_storages/mem0_cloud.py +48 -47
- camel/storages/object_storages/google_cloud.py +1 -1
- camel/storages/vectordb_storages/__init__.py +6 -0
- camel/storages/vectordb_storages/chroma.py +731 -0
- camel/storages/vectordb_storages/oceanbase.py +13 -13
- camel/storages/vectordb_storages/pgvector.py +349 -0
- camel/storages/vectordb_storages/qdrant.py +3 -3
- camel/storages/vectordb_storages/surreal.py +365 -0
- camel/storages/vectordb_storages/tidb.py +8 -6
- camel/tasks/task.py +244 -27
- camel/toolkits/__init__.py +46 -8
- camel/toolkits/aci_toolkit.py +64 -19
- camel/toolkits/arxiv_toolkit.py +6 -6
- camel/toolkits/base.py +63 -5
- camel/toolkits/code_execution.py +28 -1
- camel/toolkits/context_summarizer_toolkit.py +684 -0
- camel/toolkits/craw4ai_toolkit.py +93 -0
- camel/toolkits/dappier_toolkit.py +10 -6
- camel/toolkits/dingtalk.py +1135 -0
- camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
- camel/toolkits/excel_toolkit.py +901 -67
- camel/toolkits/file_toolkit.py +1402 -0
- camel/toolkits/function_tool.py +30 -6
- camel/toolkits/github_toolkit.py +107 -20
- camel/toolkits/gmail_toolkit.py +1839 -0
- camel/toolkits/google_calendar_toolkit.py +38 -4
- camel/toolkits/google_drive_mcp_toolkit.py +54 -0
- camel/toolkits/human_toolkit.py +34 -10
- camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
- camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
- camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +3749 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package.json +32 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1815 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +590 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
- 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 +130 -0
- camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +26 -0
- camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1032 -0
- camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
- camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
- camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
- camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
- camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
- camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
- camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
- camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
- camel/toolkits/image_generation_toolkit.py +390 -0
- camel/toolkits/jina_reranker_toolkit.py +3 -4
- camel/toolkits/klavis_toolkit.py +5 -1
- camel/toolkits/markitdown_toolkit.py +104 -0
- camel/toolkits/math_toolkit.py +64 -10
- camel/toolkits/mcp_toolkit.py +370 -45
- camel/toolkits/memory_toolkit.py +5 -1
- camel/toolkits/message_agent_toolkit.py +608 -0
- camel/toolkits/message_integration.py +724 -0
- camel/toolkits/minimax_mcp_toolkit.py +195 -0
- camel/toolkits/note_taking_toolkit.py +277 -0
- camel/toolkits/notion_mcp_toolkit.py +224 -0
- camel/toolkits/openbb_toolkit.py +5 -1
- camel/toolkits/origene_mcp_toolkit.py +56 -0
- camel/toolkits/playwright_mcp_toolkit.py +12 -31
- camel/toolkits/pptx_toolkit.py +25 -12
- camel/toolkits/resend_toolkit.py +168 -0
- camel/toolkits/screenshot_toolkit.py +213 -0
- camel/toolkits/search_toolkit.py +437 -142
- camel/toolkits/slack_toolkit.py +104 -50
- camel/toolkits/sympy_toolkit.py +1 -1
- camel/toolkits/task_planning_toolkit.py +3 -3
- camel/toolkits/terminal_toolkit/__init__.py +18 -0
- camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
- camel/toolkits/terminal_toolkit/utils.py +532 -0
- camel/toolkits/thinking_toolkit.py +1 -1
- camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
- camel/toolkits/video_analysis_toolkit.py +106 -26
- camel/toolkits/video_download_toolkit.py +17 -14
- camel/toolkits/web_deploy_toolkit.py +1219 -0
- camel/toolkits/wechat_official_toolkit.py +483 -0
- camel/toolkits/zapier_toolkit.py +5 -1
- camel/types/__init__.py +2 -2
- camel/types/agents/tool_calling_record.py +4 -1
- camel/types/enums.py +316 -40
- camel/types/openai_types.py +2 -2
- camel/types/unified_model_type.py +31 -4
- camel/utils/commons.py +36 -5
- camel/utils/constants.py +3 -0
- camel/utils/context_utils.py +1003 -0
- camel/utils/mcp.py +138 -4
- camel/utils/mcp_client.py +45 -1
- camel/utils/message_summarizer.py +148 -0
- camel/utils/token_counting.py +43 -20
- camel/utils/tool_result.py +44 -0
- {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +296 -85
- {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +219 -146
- camel/loaders/pandas_reader.py +0 -368
- camel/toolkits/dalle_toolkit.py +0 -175
- camel/toolkits/file_write_toolkit.py +0 -444
- camel/toolkits/openai_agent_toolkit.py +0 -135
- camel/toolkits/terminal_toolkit.py +0 -1037
- {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
camel/toolkits/openbb_toolkit.py
CHANGED
|
@@ -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
|
|
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(
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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)
|
camel/toolkits/pptx_toolkit.py
CHANGED
|
@@ -53,22 +53,35 @@ class PPTXToolkit(BaseToolkit):
|
|
|
53
53
|
|
|
54
54
|
def __init__(
|
|
55
55
|
self,
|
|
56
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
|
68
|
+
(default: :obj:`None`)
|
|
66
69
|
"""
|
|
67
70
|
super().__init__(timeout=timeout)
|
|
68
|
-
|
|
69
|
-
|
|
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:
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
+
]
|