camel-ai 0.2.43__py3-none-any.whl → 0.2.45__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/datasets/few_shot_generator.py +1 -0
- camel/environments/single_step.py +14 -2
- camel/extractors/python_strategies.py +14 -5
- camel/models/cohere_model.py +32 -4
- camel/models/litellm_model.py +11 -4
- camel/models/mistral_model.py +14 -2
- camel/models/reka_model.py +11 -3
- camel/models/togetherai_model.py +106 -31
- camel/models/volcano_model.py +7 -2
- camel/runtime/__init__.py +2 -0
- camel/runtime/ubuntu_docker_runtime.py +340 -0
- camel/toolkits/audio_analysis_toolkit.py +21 -17
- camel/toolkits/browser_toolkit.py +6 -1
- camel/toolkits/dalle_toolkit.py +15 -0
- camel/toolkits/excel_toolkit.py +14 -1
- camel/toolkits/image_analysis_toolkit.py +9 -1
- camel/toolkits/mcp_toolkit.py +2 -0
- camel/toolkits/networkx_toolkit.py +5 -0
- camel/toolkits/openai_agent_toolkit.py +5 -1
- camel/toolkits/searxng_toolkit.py +7 -0
- camel/toolkits/slack_toolkit.py +15 -2
- camel/toolkits/video_analysis_toolkit.py +5 -0
- camel/toolkits/weather_toolkit.py +14 -1
- camel/toolkits/zapier_toolkit.py +6 -2
- camel/types/enums.py +16 -1
- camel/verifiers/base.py +14 -0
- {camel_ai-0.2.43.dist-info → camel_ai-0.2.45.dist-info}/METADATA +5 -5
- {camel_ai-0.2.43.dist-info → camel_ai-0.2.45.dist-info}/RECORD +31 -30
- {camel_ai-0.2.43.dist-info → camel_ai-0.2.45.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.43.dist-info → camel_ai-0.2.45.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,340 @@
|
|
|
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 logging
|
|
16
|
+
import sys
|
|
17
|
+
import time
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Callable, List, Optional, Union
|
|
20
|
+
|
|
21
|
+
from camel.runtime.docker_runtime import DockerRuntime
|
|
22
|
+
from camel.toolkits import FunctionTool
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class UbuntuDockerRuntime(DockerRuntime):
|
|
28
|
+
r"""A specialized Docker runtime for Ubuntu-based environments.
|
|
29
|
+
|
|
30
|
+
This runtime includes specific configurations and setup for Ubuntu
|
|
31
|
+
containers, including proper Python path handling and environment setup.
|
|
32
|
+
It provides methods for executing Python files, managing the container
|
|
33
|
+
lifecycle, and handling file operations within the Ubuntu container.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
python_path (str): Path to the Python interpreter in the container
|
|
37
|
+
docker_config (dict): Configuration dict for Docker container setup
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
image: str,
|
|
43
|
+
port: int = 0,
|
|
44
|
+
remove: bool = True,
|
|
45
|
+
python_path: str = "/usr/bin/python3",
|
|
46
|
+
**kwargs,
|
|
47
|
+
):
|
|
48
|
+
r"""Initialize the Ubuntu Docker Runtime.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
image (str): Docker image name to use
|
|
52
|
+
port (int, optional): Port to expose. Defaults to 0 (random port)
|
|
53
|
+
remove (bool, optional): Whether to remove container after use.
|
|
54
|
+
Defaults to True
|
|
55
|
+
python_path (str, optional): Path to Python interpreter.
|
|
56
|
+
Defaults to "/usr/bin/python3"
|
|
57
|
+
**kwargs: Additional arguments passed to DockerRuntime
|
|
58
|
+
"""
|
|
59
|
+
super().__init__(image=image, port=port, remove=remove, **kwargs)
|
|
60
|
+
|
|
61
|
+
self.python_path = python_path
|
|
62
|
+
logger.info(
|
|
63
|
+
f"Initializing UbuntuDockerRuntime with python_path: {python_path}"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Set default environment variables for Ubuntu
|
|
67
|
+
self.docker_config.setdefault("environment", {})
|
|
68
|
+
self.docker_config["environment"].update(
|
|
69
|
+
{
|
|
70
|
+
"PYTHON_PATH": python_path,
|
|
71
|
+
"PYTHON_EXECUTABLE": python_path,
|
|
72
|
+
"PATH": "/usr/local/bin:/usr/bin:/bin",
|
|
73
|
+
"PYTHONUNBUFFERED": "1",
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
logger.info(
|
|
77
|
+
f"Environment variables set: {self.docker_config['environment']}"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Add default working directory
|
|
81
|
+
self.docker_config.setdefault("working_dir", "/app")
|
|
82
|
+
|
|
83
|
+
# Setup default volume mounts
|
|
84
|
+
self._setup_default_mounts()
|
|
85
|
+
|
|
86
|
+
def add(
|
|
87
|
+
self,
|
|
88
|
+
funcs: Union[FunctionTool, List[FunctionTool]],
|
|
89
|
+
entrypoint: str,
|
|
90
|
+
redirect_stdout: bool = False,
|
|
91
|
+
arguments: Optional[dict] = None,
|
|
92
|
+
) -> "UbuntuDockerRuntime":
|
|
93
|
+
r"""Add functions to the runtime with Ubuntu-specific modifications.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
funcs: Function(s) to add to the runtime
|
|
97
|
+
entrypoint: Entry point for function execution
|
|
98
|
+
redirect_stdout: Whether to redirect stdout
|
|
99
|
+
arguments: Optional arguments for function execution
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Self for method chaining
|
|
103
|
+
"""
|
|
104
|
+
if not isinstance(funcs, list):
|
|
105
|
+
funcs = [funcs]
|
|
106
|
+
|
|
107
|
+
# Modify the code execution command to use python3
|
|
108
|
+
for func in funcs:
|
|
109
|
+
logger.info(f"Processing function: {func.get_function_name()}")
|
|
110
|
+
if hasattr(func, 'command'):
|
|
111
|
+
logger.info(f"Original command: {func.command}")
|
|
112
|
+
if isinstance(func.command, list):
|
|
113
|
+
if 'python' in func.command:
|
|
114
|
+
idx = func.command.index('python')
|
|
115
|
+
func.command[idx] = self.python_path
|
|
116
|
+
logger.info(f"Modified command: {func.command}")
|
|
117
|
+
else:
|
|
118
|
+
logger.info(
|
|
119
|
+
f"No command attribute found for function "
|
|
120
|
+
f"{func.get_function_name()}"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
super().add(funcs, entrypoint, redirect_stdout, arguments)
|
|
124
|
+
return self
|
|
125
|
+
|
|
126
|
+
def _setup_default_mounts(self):
|
|
127
|
+
r"""Setup default volume mounts for the container.
|
|
128
|
+
|
|
129
|
+
This method can be extended to add Ubuntu-specific volume mounts.
|
|
130
|
+
"""
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
def build(self, time_out: int = 15) -> "UbuntuDockerRuntime":
|
|
134
|
+
r"""Build and initialize the Ubuntu container with proper setup.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
time_out (int): Timeout in seconds for build operation
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Self for method chaining
|
|
141
|
+
"""
|
|
142
|
+
logger.info("Starting container build...")
|
|
143
|
+
|
|
144
|
+
super().build(time_out=time_out)
|
|
145
|
+
|
|
146
|
+
if self.container:
|
|
147
|
+
logger.info("Container built successfully, verifying setup...")
|
|
148
|
+
|
|
149
|
+
# Verify Python installation
|
|
150
|
+
exit_code, output = self.container.exec_run(
|
|
151
|
+
[self.python_path, "--version"]
|
|
152
|
+
)
|
|
153
|
+
logger.info(f"Python version check result: {output.decode()}")
|
|
154
|
+
if exit_code != 0:
|
|
155
|
+
logger.error(
|
|
156
|
+
f"Python version check failed with exit code {exit_code}"
|
|
157
|
+
)
|
|
158
|
+
raise RuntimeError(
|
|
159
|
+
f"Python installation verification "
|
|
160
|
+
f"failed: {output.decode()}"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Install required packages
|
|
164
|
+
logger.info("Installing required packages...")
|
|
165
|
+
exit_code, output = self.container.exec_run("apt-get update")
|
|
166
|
+
if exit_code != 0:
|
|
167
|
+
logger.error(
|
|
168
|
+
f"apt-get update failed with "
|
|
169
|
+
f"exit code {exit_code}: {output.decode()}"
|
|
170
|
+
)
|
|
171
|
+
raise RuntimeError(
|
|
172
|
+
f"Failed to update package lists: {output.decode()}"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
exit_code, output = self.container.exec_run(
|
|
176
|
+
"apt-get install -y curl"
|
|
177
|
+
)
|
|
178
|
+
if exit_code != 0:
|
|
179
|
+
logger.error(
|
|
180
|
+
f"apt-get install curl failed with "
|
|
181
|
+
f"exit code {exit_code}: {output.decode()}"
|
|
182
|
+
)
|
|
183
|
+
raise RuntimeError(
|
|
184
|
+
f"Failed to install curl: {output.decode()}"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Start API server with explicit Python path
|
|
188
|
+
logger.info("Starting API server...")
|
|
189
|
+
exec_result = self.container.exec_run(
|
|
190
|
+
[self.python_path, "/home/api.py"],
|
|
191
|
+
detach=True,
|
|
192
|
+
environment={
|
|
193
|
+
"PYTHONPATH": str(
|
|
194
|
+
Path(self.python_path).parent
|
|
195
|
+
/ "lib/python3.10/site-packages"
|
|
196
|
+
),
|
|
197
|
+
"PYTHON_EXECUTABLE": self.python_path,
|
|
198
|
+
},
|
|
199
|
+
)
|
|
200
|
+
logger.info("API server start result: %s", exec_result)
|
|
201
|
+
|
|
202
|
+
# Wait for API server to start
|
|
203
|
+
start_time = time.time()
|
|
204
|
+
while time.time() - start_time < 10:
|
|
205
|
+
try:
|
|
206
|
+
exit_code, curl_result = self.container.exec_run(
|
|
207
|
+
"curl -s -o /dev/null -w '%{http_code}' http://localhost:8000/docs"
|
|
208
|
+
)
|
|
209
|
+
status_code = curl_result.decode().strip()
|
|
210
|
+
if exit_code == 0 and status_code.startswith('2'):
|
|
211
|
+
logger.info(
|
|
212
|
+
f"API server is running "
|
|
213
|
+
f"(status code: {status_code})"
|
|
214
|
+
)
|
|
215
|
+
break
|
|
216
|
+
else:
|
|
217
|
+
logger.debug(
|
|
218
|
+
f"API server not ready yet (status: {status_code})"
|
|
219
|
+
)
|
|
220
|
+
except Exception as e:
|
|
221
|
+
logger.debug("Waiting for API server... %s", e)
|
|
222
|
+
time.sleep(0.5)
|
|
223
|
+
else:
|
|
224
|
+
logger.warning("API server may not be running properly")
|
|
225
|
+
|
|
226
|
+
return self
|
|
227
|
+
|
|
228
|
+
def exec_python_file(
|
|
229
|
+
self,
|
|
230
|
+
local_file_path: str,
|
|
231
|
+
container_path: Optional[str] = None,
|
|
232
|
+
args: Optional[List[str]] = None,
|
|
233
|
+
env: Optional[dict] = None,
|
|
234
|
+
callback: Optional[Callable[[str], None]] = None,
|
|
235
|
+
) -> None:
|
|
236
|
+
r"""Execute a Python file inside the Docker container.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
local_file_path: Path to the Python file on the local filesystem
|
|
240
|
+
container_path: Path where the file should be copied in the
|
|
241
|
+
container If None, the file will be copied to /tmp/
|
|
242
|
+
args: List of command-line arguments to pass to the Python script
|
|
243
|
+
env: Additional environment variables to set for the execution
|
|
244
|
+
callback: Optional function to process each line of output
|
|
245
|
+
If None, output is printed to stdout
|
|
246
|
+
|
|
247
|
+
Raises:
|
|
248
|
+
RuntimeError: If container is not running
|
|
249
|
+
FileNotFoundError: If Python file is not found
|
|
250
|
+
"""
|
|
251
|
+
if not self.container:
|
|
252
|
+
raise RuntimeError("Container is not running. Call build() first.")
|
|
253
|
+
|
|
254
|
+
local_path = Path(local_file_path)
|
|
255
|
+
if not local_path.exists():
|
|
256
|
+
raise FileNotFoundError(f"Python file {local_file_path} not found")
|
|
257
|
+
|
|
258
|
+
# Determine where to put the file in the container
|
|
259
|
+
if container_path is None:
|
|
260
|
+
container_path = f"/tmp/{local_path.name}"
|
|
261
|
+
|
|
262
|
+
logger.info(
|
|
263
|
+
f"Copying {local_file_path} to container at {container_path}"
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# Copy the file to the container
|
|
267
|
+
self.container.put_archive(
|
|
268
|
+
path=str(Path(container_path).parent),
|
|
269
|
+
data=self._create_archive_from_file(local_path),
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Prepare command
|
|
273
|
+
cmd = [self.python_path, container_path]
|
|
274
|
+
if args:
|
|
275
|
+
cmd.extend(args)
|
|
276
|
+
|
|
277
|
+
# Prepare environment
|
|
278
|
+
execution_env = {
|
|
279
|
+
"PYTHONPATH": "/usr/local/lib/python3.10/site-packages",
|
|
280
|
+
"PYTHON_EXECUTABLE": self.python_path,
|
|
281
|
+
}
|
|
282
|
+
execution_env["PYTHONPATH"] = str(
|
|
283
|
+
Path(self.python_path).parent / "lib/python3.10/site-packages"
|
|
284
|
+
)
|
|
285
|
+
if env:
|
|
286
|
+
execution_env.update(env)
|
|
287
|
+
|
|
288
|
+
logger.info(f"Executing Python file with command: {cmd}")
|
|
289
|
+
|
|
290
|
+
# Always use streaming output
|
|
291
|
+
exec_result = self.container.exec_run(
|
|
292
|
+
cmd,
|
|
293
|
+
environment=execution_env,
|
|
294
|
+
stream=True,
|
|
295
|
+
demux=True, # Separate stdout and stderr
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# Handle output streams
|
|
299
|
+
try:
|
|
300
|
+
for stdout, stderr in exec_result[1]:
|
|
301
|
+
if stdout:
|
|
302
|
+
output = stdout.decode('utf-8')
|
|
303
|
+
if callback:
|
|
304
|
+
callback(output)
|
|
305
|
+
else:
|
|
306
|
+
print(output, end='')
|
|
307
|
+
|
|
308
|
+
if stderr:
|
|
309
|
+
error = stderr.decode('utf-8')
|
|
310
|
+
if callback:
|
|
311
|
+
callback(f"ERROR: {error}")
|
|
312
|
+
else:
|
|
313
|
+
print(f"ERROR: {error}", end='', file=sys.stderr)
|
|
314
|
+
except KeyboardInterrupt:
|
|
315
|
+
logger.info("Execution interrupted by user")
|
|
316
|
+
# Could add logic to stop container processes here
|
|
317
|
+
except Exception as e:
|
|
318
|
+
logger.error(f"Error during execution: {e}")
|
|
319
|
+
raise
|
|
320
|
+
|
|
321
|
+
def _create_archive_from_file(self, file_path: Union[str, Path]) -> bytes:
|
|
322
|
+
r"""Create a tar archive from a single file for docker.put_archive().
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
file_path: Path to the file to archive
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
bytes: The tar archive as bytes
|
|
329
|
+
"""
|
|
330
|
+
import io
|
|
331
|
+
import tarfile
|
|
332
|
+
|
|
333
|
+
file_path = Path(file_path)
|
|
334
|
+
tar_stream = io.BytesIO()
|
|
335
|
+
|
|
336
|
+
with tarfile.open(fileobj=tar_stream, mode='w') as tar:
|
|
337
|
+
tar.add(file_path, arcname=file_path.name)
|
|
338
|
+
|
|
339
|
+
tar_stream.seek(0)
|
|
340
|
+
return tar_stream.read()
|
|
@@ -83,29 +83,33 @@ def download_file(url: str, cache_dir: str) -> str:
|
|
|
83
83
|
|
|
84
84
|
@MCPServer()
|
|
85
85
|
class AudioAnalysisToolkit(BaseToolkit):
|
|
86
|
-
r"""A toolkit for audio processing and analysis.
|
|
87
|
-
|
|
88
|
-
This class provides methods for processing, transcribing, and extracting
|
|
89
|
-
information from audio data, including direct question answering about
|
|
90
|
-
audio content.
|
|
91
|
-
|
|
92
|
-
Args:
|
|
93
|
-
cache_dir (Optional[str]): Directory path for caching downloaded audio
|
|
94
|
-
files. If not provided, 'tmp/' will be used. (default: :obj:`None`)
|
|
95
|
-
transcribe_model (Optional[BaseAudioModel]): Model used for audio
|
|
96
|
-
transcription. If not provided, OpenAIAudioModels will be used.
|
|
97
|
-
(default: :obj:`None`)
|
|
98
|
-
audio_reasoning_model (Optional[BaseModelBackend]): Model used for
|
|
99
|
-
audio reasoning and question answering. If not provided, uses the
|
|
100
|
-
default model from ChatAgent. (default: :obj:`None`)
|
|
101
|
-
"""
|
|
102
|
-
|
|
103
86
|
def __init__(
|
|
104
87
|
self,
|
|
105
88
|
cache_dir: Optional[str] = None,
|
|
106
89
|
transcribe_model: Optional[BaseAudioModel] = None,
|
|
107
90
|
audio_reasoning_model: Optional[BaseModelBackend] = None,
|
|
91
|
+
timeout: Optional[float] = None,
|
|
108
92
|
):
|
|
93
|
+
r"""A toolkit for audio processing and analysis. This class provides
|
|
94
|
+
methods for processing, transcribing, and extracting information from
|
|
95
|
+
audio data, including direct question answering about audio content.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
cache_dir (Optional[str]): Directory path for caching downloaded
|
|
99
|
+
audio files. If not provided, 'tmp/' will be used.
|
|
100
|
+
(default: :obj:`None`)
|
|
101
|
+
transcribe_model (Optional[BaseAudioModel]): Model used for audio
|
|
102
|
+
transcription. If not provided, OpenAIAudioModels will be used.
|
|
103
|
+
(default: :obj:`None`)
|
|
104
|
+
audio_reasoning_model (Optional[BaseModelBackend]): Model used for
|
|
105
|
+
audio reasoning and question answering.
|
|
106
|
+
If not provided, uses the default model from ChatAgent.
|
|
107
|
+
(default: :obj:`None`)
|
|
108
|
+
timeout (Optional[float]): The timeout value for API requests
|
|
109
|
+
in seconds. If None, no timeout is applied.
|
|
110
|
+
(default: :obj:`None`)
|
|
111
|
+
"""
|
|
112
|
+
super().__init__(timeout=timeout)
|
|
109
113
|
self.cache_dir = 'tmp/'
|
|
110
114
|
if cache_dir:
|
|
111
115
|
self.cache_dir = cache_dir
|
|
@@ -43,8 +43,9 @@ if TYPE_CHECKING:
|
|
|
43
43
|
from camel.logger import get_logger
|
|
44
44
|
from camel.messages import BaseMessage
|
|
45
45
|
from camel.models import BaseModelBackend, ModelFactory
|
|
46
|
-
from camel.toolkits import FunctionTool, VideoAnalysisToolkit
|
|
47
46
|
from camel.toolkits.base import BaseToolkit
|
|
47
|
+
from camel.toolkits.function_tool import FunctionTool
|
|
48
|
+
from camel.toolkits.video_analysis_toolkit import VideoAnalysisToolkit
|
|
48
49
|
from camel.types import ModelPlatformType, ModelType
|
|
49
50
|
from camel.utils import dependencies_required, retry_on_error
|
|
50
51
|
|
|
@@ -1175,6 +1176,8 @@ out the information you need. Sometimes they are extremely useful.
|
|
|
1175
1176
|
message = BaseMessage.make_user_message(
|
|
1176
1177
|
role_name='user', content=observe_prompt, image_list=[img]
|
|
1177
1178
|
)
|
|
1179
|
+
# Reset the history message of web_agent.
|
|
1180
|
+
self.web_agent.reset()
|
|
1178
1181
|
resp = self.web_agent.step(message)
|
|
1179
1182
|
|
|
1180
1183
|
resp_content = resp.msgs[0].content
|
|
@@ -1404,6 +1407,8 @@ Your output should be in json format, including the following fields:
|
|
|
1404
1407
|
- `if_need_replan`: bool, A boolean value indicating whether the task needs to be fundamentally replanned.
|
|
1405
1408
|
- `replanned_schema`: str, The replanned schema for the task, which should not be changed too much compared with the original one. If the task does not need to be replanned, the value should be an empty string.
|
|
1406
1409
|
"""
|
|
1410
|
+
# Reset the history message of planning_agent.
|
|
1411
|
+
self.planning_agent.reset()
|
|
1407
1412
|
resp = self.planning_agent.step(replanning_prompt)
|
|
1408
1413
|
resp_dict = _parse_json_output(resp.msgs[0].content)
|
|
1409
1414
|
|
camel/toolkits/dalle_toolkit.py
CHANGED
|
@@ -22,13 +22,28 @@ from PIL import Image
|
|
|
22
22
|
|
|
23
23
|
from camel.toolkits import FunctionTool
|
|
24
24
|
from camel.toolkits.base import BaseToolkit
|
|
25
|
+
from camel.utils import MCPServer
|
|
25
26
|
|
|
26
27
|
|
|
28
|
+
@MCPServer()
|
|
27
29
|
class DalleToolkit(BaseToolkit):
|
|
28
30
|
r"""A class representing a toolkit for image generation using OpenAI's
|
|
29
31
|
DALL-E model.
|
|
30
32
|
"""
|
|
31
33
|
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
timeout: Optional[float] = None,
|
|
37
|
+
):
|
|
38
|
+
r"""Initializes a new instance of the DalleToolkit class.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
timeout (Optional[float]): The timeout value for API requests
|
|
42
|
+
in seconds. If None, no timeout is applied.
|
|
43
|
+
(default: :obj:`None`)
|
|
44
|
+
"""
|
|
45
|
+
super().__init__(timeout=timeout)
|
|
46
|
+
|
|
32
47
|
def base64_to_image(self, base64_string: str) -> Optional[Image.Image]:
|
|
33
48
|
r"""Converts a base64 encoded string into a PIL Image object.
|
|
34
49
|
|
camel/toolkits/excel_toolkit.py
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
|
|
15
|
-
from typing import List
|
|
15
|
+
from typing import List, Optional
|
|
16
16
|
|
|
17
17
|
import pandas as pd
|
|
18
18
|
|
|
@@ -33,6 +33,19 @@ class ExcelToolkit(BaseToolkit):
|
|
|
33
33
|
process excel files.
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
timeout: Optional[float] = None,
|
|
39
|
+
):
|
|
40
|
+
r"""Initializes a new instance of the ExcelToolkit class.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
timeout (Optional[float]): The timeout value for API requests
|
|
44
|
+
in seconds. If None, no timeout is applied.
|
|
45
|
+
(default: :obj:`None`)
|
|
46
|
+
"""
|
|
47
|
+
super().__init__(timeout=timeout)
|
|
48
|
+
|
|
36
49
|
def _convert_to_markdown(self, df: pd.DataFrame) -> str:
|
|
37
50
|
r"""Convert DataFrame to Markdown format table.
|
|
38
51
|
|
|
@@ -36,7 +36,11 @@ class ImageAnalysisToolkit(BaseToolkit):
|
|
|
36
36
|
The toolkit uses vision-capable language models to perform these tasks.
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
|
-
def __init__(
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
model: Optional[BaseModelBackend] = None,
|
|
42
|
+
timeout: Optional[float] = None,
|
|
43
|
+
):
|
|
40
44
|
r"""Initialize the ImageAnalysisToolkit.
|
|
41
45
|
|
|
42
46
|
Args:
|
|
@@ -45,7 +49,11 @@ class ImageAnalysisToolkit(BaseToolkit):
|
|
|
45
49
|
images for tasks like image description and visual question
|
|
46
50
|
answering. If None, a default model will be created using
|
|
47
51
|
ModelFactory. (default: :obj:`None`)
|
|
52
|
+
timeout (Optional[float]): The timeout value for API requests
|
|
53
|
+
in seconds. If None, no timeout is applied.
|
|
54
|
+
(default: :obj:`None`)
|
|
48
55
|
"""
|
|
56
|
+
super().__init__(timeout=timeout)
|
|
49
57
|
if model:
|
|
50
58
|
self.model = model
|
|
51
59
|
else:
|
camel/toolkits/mcp_toolkit.py
CHANGED
|
@@ -314,6 +314,7 @@ class MCPClient(BaseToolkit):
|
|
|
314
314
|
"type": "object",
|
|
315
315
|
"properties": properties,
|
|
316
316
|
"required": required,
|
|
317
|
+
"additionalProperties": False,
|
|
317
318
|
}
|
|
318
319
|
|
|
319
320
|
return {
|
|
@@ -322,6 +323,7 @@ class MCPClient(BaseToolkit):
|
|
|
322
323
|
"name": mcp_tool.name,
|
|
323
324
|
"description": mcp_tool.description
|
|
324
325
|
or "No description provided.",
|
|
326
|
+
"strict": True,
|
|
325
327
|
"parameters": parameters,
|
|
326
328
|
},
|
|
327
329
|
}
|
|
@@ -38,6 +38,7 @@ class NetworkXToolkit(BaseToolkit):
|
|
|
38
38
|
|
|
39
39
|
def __init__(
|
|
40
40
|
self,
|
|
41
|
+
timeout: Optional[float] = None,
|
|
41
42
|
graph_type: Literal[
|
|
42
43
|
'graph', 'digraph', 'multigraph', 'multidigraph'
|
|
43
44
|
] = 'graph',
|
|
@@ -53,7 +54,11 @@ class NetworkXToolkit(BaseToolkit):
|
|
|
53
54
|
- 'multigraph': Undirected graph with parallel edges
|
|
54
55
|
- 'multidigraph': Directed graph with parallel edges
|
|
55
56
|
(default: :obj:`'graph'`)
|
|
57
|
+
timeout (Optional[float]): The timeout value for API requests
|
|
58
|
+
in seconds. If None, no timeout is applied.
|
|
59
|
+
(default: :obj:`None`)
|
|
56
60
|
"""
|
|
61
|
+
super().__init__(timeout=timeout)
|
|
57
62
|
nx = self._get_nx()
|
|
58
63
|
graph_types = {
|
|
59
64
|
'graph': nx.Graph,
|
|
@@ -45,6 +45,7 @@ class OpenAIAgentToolkit(BaseToolkit):
|
|
|
45
45
|
self,
|
|
46
46
|
model: Optional[BaseModelBackend] = None,
|
|
47
47
|
api_key: Optional[str] = None,
|
|
48
|
+
timeout: Optional[float] = None,
|
|
48
49
|
) -> None:
|
|
49
50
|
r"""Initialize the OpenAI agent toolkit.
|
|
50
51
|
|
|
@@ -53,8 +54,11 @@ class OpenAIAgentToolkit(BaseToolkit):
|
|
|
53
54
|
If None, defaults to gpt-4o-mini. (default: :obj:`None`)
|
|
54
55
|
api_key (str): OpenAI API key. If not provided, will attempt to
|
|
55
56
|
use OPENAI_API_KEY environment variable. (default: :obj:`None`)
|
|
57
|
+
timeout (Optional[float]): The timeout value for API requests
|
|
58
|
+
in seconds. If None, no timeout is applied.
|
|
59
|
+
(default: :obj:`None`)
|
|
56
60
|
"""
|
|
57
|
-
super().__init__()
|
|
61
|
+
super().__init__(timeout=timeout)
|
|
58
62
|
self.api_key = api_key or os.getenv("OPENAI_API_KEY")
|
|
59
63
|
self.client = OpenAI(api_key=self.api_key)
|
|
60
64
|
self.model = model or ModelFactory.create(
|
|
@@ -20,10 +20,12 @@ import requests
|
|
|
20
20
|
from camel.logger import get_logger
|
|
21
21
|
from camel.toolkits.base import BaseToolkit
|
|
22
22
|
from camel.toolkits.function_tool import FunctionTool
|
|
23
|
+
from camel.utils import MCPServer
|
|
23
24
|
|
|
24
25
|
logger = get_logger(__name__)
|
|
25
26
|
|
|
26
27
|
|
|
28
|
+
@MCPServer()
|
|
27
29
|
class SearxNGToolkit(BaseToolkit):
|
|
28
30
|
r"""A toolkit for performing web searches using SearxNG search engine.
|
|
29
31
|
|
|
@@ -42,6 +44,9 @@ class SearxNGToolkit(BaseToolkit):
|
|
|
42
44
|
values are "day", "week", "month", "year". (default: :obj:`None`)
|
|
43
45
|
safe_search (int, optional): Safe search level (0: None, 1: Moderate,
|
|
44
46
|
2: Strict). (default: :obj:`1`)
|
|
47
|
+
timeout (Optional[float]): The timeout value for API requests
|
|
48
|
+
in seconds. If None, no timeout is applied.
|
|
49
|
+
(default: :obj:`None`)
|
|
45
50
|
|
|
46
51
|
Raises:
|
|
47
52
|
ValueError: If searxng_host is not a valid HTTP/HTTPS URL.
|
|
@@ -65,7 +70,9 @@ class SearxNGToolkit(BaseToolkit):
|
|
|
65
70
|
categories: Optional[List[str]] = None,
|
|
66
71
|
time_range: Optional[str] = None,
|
|
67
72
|
safe_search: int = 1,
|
|
73
|
+
timeout: Optional[float] = None,
|
|
68
74
|
) -> None:
|
|
75
|
+
super().__init__(timeout=timeout)
|
|
69
76
|
self._validate_searxng_host(searxng_host)
|
|
70
77
|
self._validate_safe_search(safe_search)
|
|
71
78
|
if time_range is not None:
|
camel/toolkits/slack_toolkit.py
CHANGED
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
import json
|
|
18
|
-
import logging
|
|
19
18
|
import os
|
|
20
19
|
from typing import TYPE_CHECKING, List, Optional
|
|
21
20
|
|
|
@@ -27,9 +26,10 @@ if TYPE_CHECKING:
|
|
|
27
26
|
|
|
28
27
|
from slack_sdk import WebClient
|
|
29
28
|
|
|
29
|
+
from camel.logger import get_logger
|
|
30
30
|
from camel.toolkits import FunctionTool
|
|
31
31
|
|
|
32
|
-
logger =
|
|
32
|
+
logger = get_logger(__name__)
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
@MCPServer()
|
|
@@ -40,6 +40,19 @@ class SlackToolkit(BaseToolkit):
|
|
|
40
40
|
channel, joining an existing channel, leaving a channel.
|
|
41
41
|
"""
|
|
42
42
|
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
timeout: Optional[float] = None,
|
|
46
|
+
):
|
|
47
|
+
r"""Initializes a new instance of the SlackToolkit class.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
timeout (Optional[float]): The timeout value for API requests
|
|
51
|
+
in seconds. If None, no timeout is applied.
|
|
52
|
+
(default: :obj:`None`)
|
|
53
|
+
"""
|
|
54
|
+
super().__init__(timeout=timeout)
|
|
55
|
+
|
|
43
56
|
def _login_slack(
|
|
44
57
|
self,
|
|
45
58
|
slack_token: Optional[str] = None,
|
|
@@ -92,6 +92,9 @@ class VideoAnalysisToolkit(BaseToolkit):
|
|
|
92
92
|
transcription using OpenAI's audio models. Requires a valid OpenAI
|
|
93
93
|
API key. When disabled, video analysis will be based solely on
|
|
94
94
|
visual content. (default: :obj:`False`)
|
|
95
|
+
timeout (Optional[float]): The timeout value for API requests
|
|
96
|
+
in seconds. If None, no timeout is applied.
|
|
97
|
+
(default: :obj:`None`)
|
|
95
98
|
"""
|
|
96
99
|
|
|
97
100
|
@dependencies_required("ffmpeg", "scenedetect")
|
|
@@ -100,7 +103,9 @@ class VideoAnalysisToolkit(BaseToolkit):
|
|
|
100
103
|
download_directory: Optional[str] = None,
|
|
101
104
|
model: Optional[BaseModelBackend] = None,
|
|
102
105
|
use_audio_transcription: bool = False,
|
|
106
|
+
timeout: Optional[float] = None,
|
|
103
107
|
) -> None:
|
|
108
|
+
super().__init__(timeout=timeout)
|
|
104
109
|
self._cleanup = download_directory is None
|
|
105
110
|
self._temp_files: list[str] = [] # Track temporary files for cleanup
|
|
106
111
|
self._use_audio_transcription = use_audio_transcription
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
import os
|
|
15
|
-
from typing import List, Literal
|
|
15
|
+
from typing import List, Literal, Optional
|
|
16
16
|
|
|
17
17
|
from camel.toolkits.base import BaseToolkit
|
|
18
18
|
from camel.toolkits.function_tool import FunctionTool
|
|
@@ -27,6 +27,19 @@ class WeatherToolkit(BaseToolkit):
|
|
|
27
27
|
using the OpenWeatherMap API.
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
timeout: Optional[float] = None,
|
|
33
|
+
):
|
|
34
|
+
r"""Initializes a new instance of the WeatherToolkit class.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
timeout (Optional[float]): The timeout value for API requests
|
|
38
|
+
in seconds. If None, no timeout is applied.
|
|
39
|
+
(default: :obj:`None`)
|
|
40
|
+
"""
|
|
41
|
+
super().__init__(timeout=timeout)
|
|
42
|
+
|
|
30
43
|
def get_openweathermap_api_key(self) -> str:
|
|
31
44
|
r"""Retrieve the OpenWeatherMap API key from environment variables.
|
|
32
45
|
|