camel-ai 0.2.75a5__py3-none-any.whl → 0.2.76a0__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 +298 -130
- camel/configs/__init__.py +6 -0
- camel/configs/amd_config.py +70 -0
- camel/configs/nebius_config.py +103 -0
- camel/interpreters/__init__.py +2 -0
- camel/interpreters/microsandbox_interpreter.py +395 -0
- camel/models/__init__.py +4 -0
- camel/models/amd_model.py +101 -0
- camel/models/model_factory.py +4 -0
- camel/models/nebius_model.py +83 -0
- camel/models/ollama_model.py +3 -3
- camel/models/openai_model.py +0 -6
- camel/runtimes/daytona_runtime.py +11 -12
- camel/societies/workforce/task_channel.py +120 -27
- camel/societies/workforce/workforce.py +35 -3
- camel/toolkits/__init__.py +5 -3
- camel/toolkits/code_execution.py +28 -1
- camel/toolkits/function_tool.py +6 -1
- camel/toolkits/github_toolkit.py +104 -17
- camel/toolkits/hybrid_browser_toolkit/config_loader.py +8 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +12 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +33 -14
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +135 -40
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +2 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +43 -207
- camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +231 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
- camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +39 -6
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +248 -58
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +5 -1
- camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +98 -31
- camel/toolkits/math_toolkit.py +64 -10
- camel/toolkits/mcp_toolkit.py +39 -14
- camel/toolkits/minimax_mcp_toolkit.py +195 -0
- camel/toolkits/search_toolkit.py +13 -2
- camel/toolkits/terminal_toolkit.py +12 -2
- camel/toolkits/video_analysis_toolkit.py +16 -10
- camel/types/enums.py +42 -0
- camel/types/unified_model_type.py +5 -0
- camel/utils/commons.py +2 -0
- camel/utils/mcp.py +136 -2
- {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/METADATA +5 -11
- {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/RECORD +47 -38
- {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/licenses/LICENSE +0 -0
camel/toolkits/math_toolkit.py
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
|
|
15
|
+
import warnings
|
|
15
16
|
from typing import List
|
|
16
17
|
|
|
17
18
|
from camel.toolkits.base import BaseToolkit
|
|
@@ -27,7 +28,7 @@ class MathToolkit(BaseToolkit):
|
|
|
27
28
|
addition, subtraction, multiplication, division, and rounding.
|
|
28
29
|
"""
|
|
29
30
|
|
|
30
|
-
def
|
|
31
|
+
def math_add(self, a: float, b: float) -> float:
|
|
31
32
|
r"""Adds two numbers.
|
|
32
33
|
|
|
33
34
|
Args:
|
|
@@ -39,7 +40,7 @@ class MathToolkit(BaseToolkit):
|
|
|
39
40
|
"""
|
|
40
41
|
return a + b
|
|
41
42
|
|
|
42
|
-
def
|
|
43
|
+
def math_subtract(self, a: float, b: float) -> float:
|
|
43
44
|
r"""Do subtraction between two numbers.
|
|
44
45
|
|
|
45
46
|
Args:
|
|
@@ -51,7 +52,9 @@ class MathToolkit(BaseToolkit):
|
|
|
51
52
|
"""
|
|
52
53
|
return a - b
|
|
53
54
|
|
|
54
|
-
def
|
|
55
|
+
def math_multiply(
|
|
56
|
+
self, a: float, b: float, decimal_places: int = 2
|
|
57
|
+
) -> float:
|
|
55
58
|
r"""Multiplies two numbers.
|
|
56
59
|
|
|
57
60
|
Args:
|
|
@@ -65,7 +68,9 @@ class MathToolkit(BaseToolkit):
|
|
|
65
68
|
"""
|
|
66
69
|
return round(a * b, decimal_places)
|
|
67
70
|
|
|
68
|
-
def
|
|
71
|
+
def math_divide(
|
|
72
|
+
self, a: float, b: float, decimal_places: int = 2
|
|
73
|
+
) -> float:
|
|
69
74
|
r"""Divides two numbers.
|
|
70
75
|
|
|
71
76
|
Args:
|
|
@@ -79,7 +84,7 @@ class MathToolkit(BaseToolkit):
|
|
|
79
84
|
"""
|
|
80
85
|
return round(a / b, decimal_places)
|
|
81
86
|
|
|
82
|
-
def
|
|
87
|
+
def math_round(self, a: float, decimal_places: int = 0) -> float:
|
|
83
88
|
r"""Rounds a number to a specified number of decimal places.
|
|
84
89
|
|
|
85
90
|
Args:
|
|
@@ -101,9 +106,58 @@ class MathToolkit(BaseToolkit):
|
|
|
101
106
|
representing the functions in the toolkit.
|
|
102
107
|
"""
|
|
103
108
|
return [
|
|
104
|
-
FunctionTool(self.
|
|
105
|
-
FunctionTool(self.
|
|
106
|
-
FunctionTool(self.
|
|
107
|
-
FunctionTool(self.
|
|
108
|
-
FunctionTool(self.
|
|
109
|
+
FunctionTool(self.math_add),
|
|
110
|
+
FunctionTool(self.math_subtract),
|
|
111
|
+
FunctionTool(self.math_multiply),
|
|
112
|
+
FunctionTool(self.math_divide),
|
|
113
|
+
FunctionTool(self.math_round),
|
|
109
114
|
]
|
|
115
|
+
|
|
116
|
+
# Deprecated method aliases for backward compatibility
|
|
117
|
+
def add(self, *args, **kwargs):
|
|
118
|
+
r"""Deprecated: Use math_add instead."""
|
|
119
|
+
warnings.warn(
|
|
120
|
+
"add is deprecated. Use math_add instead.",
|
|
121
|
+
DeprecationWarning,
|
|
122
|
+
stacklevel=2,
|
|
123
|
+
)
|
|
124
|
+
return self.math_add(*args, **kwargs)
|
|
125
|
+
|
|
126
|
+
def sub(self, *args, **kwargs):
|
|
127
|
+
r"""Deprecated: Use math_subtract instead."""
|
|
128
|
+
warnings.warn(
|
|
129
|
+
"sub is deprecated. Use math_subtract instead.",
|
|
130
|
+
DeprecationWarning,
|
|
131
|
+
stacklevel=2,
|
|
132
|
+
)
|
|
133
|
+
return self.math_subtract(*args, **kwargs)
|
|
134
|
+
|
|
135
|
+
def multiply(self, *args, **kwargs):
|
|
136
|
+
r"""Deprecated: Use math_multiply instead."""
|
|
137
|
+
warnings.warn(
|
|
138
|
+
"multiply is deprecated. Use math_multiply instead.",
|
|
139
|
+
DeprecationWarning,
|
|
140
|
+
stacklevel=2,
|
|
141
|
+
)
|
|
142
|
+
return self.math_multiply(*args, **kwargs)
|
|
143
|
+
|
|
144
|
+
def divide(self, *args, **kwargs):
|
|
145
|
+
r"""Deprecated: Use math_divide instead."""
|
|
146
|
+
warnings.warn(
|
|
147
|
+
"divide is deprecated. Use math_divide instead.",
|
|
148
|
+
DeprecationWarning,
|
|
149
|
+
stacklevel=2,
|
|
150
|
+
)
|
|
151
|
+
return self.math_divide(*args, **kwargs)
|
|
152
|
+
|
|
153
|
+
def round(self, *args, **kwargs):
|
|
154
|
+
r"""Deprecated: Use math_round instead. Note: This was shadowing
|
|
155
|
+
Python's built-in round().
|
|
156
|
+
"""
|
|
157
|
+
warnings.warn(
|
|
158
|
+
"round is deprecated. Use math_round instead. This was "
|
|
159
|
+
"shadowing Python's built-in round().",
|
|
160
|
+
DeprecationWarning,
|
|
161
|
+
stacklevel=2,
|
|
162
|
+
)
|
|
163
|
+
return self.math_round(*args, **kwargs)
|
camel/toolkits/mcp_toolkit.py
CHANGED
|
@@ -220,26 +220,34 @@ class MCPToolkit(BaseToolkit):
|
|
|
220
220
|
self._exit_stack = AsyncExitStack()
|
|
221
221
|
|
|
222
222
|
try:
|
|
223
|
-
#
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
except Exception as e:
|
|
231
|
-
logger.error(f"Failed to connect to client {i+1}: {e}")
|
|
232
|
-
# AsyncExitStack will handle cleanup of already connected
|
|
233
|
-
await self._exit_stack.aclose()
|
|
234
|
-
self._exit_stack = None
|
|
235
|
-
error_msg = f"Failed to connect to client {i+1}: {e}"
|
|
236
|
-
raise MCPConnectionError(error_msg) from e
|
|
223
|
+
# Apply timeout to the entire connection process
|
|
224
|
+
import asyncio
|
|
225
|
+
|
|
226
|
+
timeout_seconds = self.timeout or 30.0
|
|
227
|
+
await asyncio.wait_for(
|
|
228
|
+
self._connect_all_clients(), timeout=timeout_seconds
|
|
229
|
+
)
|
|
237
230
|
|
|
238
231
|
self._is_connected = True
|
|
239
232
|
msg = f"Successfully connected to {len(self.clients)} MCP servers"
|
|
240
233
|
logger.info(msg)
|
|
241
234
|
return self
|
|
242
235
|
|
|
236
|
+
except (asyncio.TimeoutError, asyncio.CancelledError):
|
|
237
|
+
self._is_connected = False
|
|
238
|
+
if self._exit_stack:
|
|
239
|
+
await self._exit_stack.aclose()
|
|
240
|
+
self._exit_stack = None
|
|
241
|
+
|
|
242
|
+
timeout_seconds = self.timeout or 30.0
|
|
243
|
+
error_msg = (
|
|
244
|
+
f"Connection timeout after {timeout_seconds}s. "
|
|
245
|
+
f"One or more MCP servers are not responding. "
|
|
246
|
+
f"Please check if the servers are running and accessible."
|
|
247
|
+
)
|
|
248
|
+
logger.error(error_msg)
|
|
249
|
+
raise MCPConnectionError(error_msg)
|
|
250
|
+
|
|
243
251
|
except Exception:
|
|
244
252
|
self._is_connected = False
|
|
245
253
|
if self._exit_stack:
|
|
@@ -247,6 +255,23 @@ class MCPToolkit(BaseToolkit):
|
|
|
247
255
|
self._exit_stack = None
|
|
248
256
|
raise
|
|
249
257
|
|
|
258
|
+
async def _connect_all_clients(self):
|
|
259
|
+
r"""Connect to all clients sequentially."""
|
|
260
|
+
# Connect to all clients using AsyncExitStack
|
|
261
|
+
for i, client in enumerate(self.clients):
|
|
262
|
+
try:
|
|
263
|
+
# Use MCPClient directly as async context manager
|
|
264
|
+
await self._exit_stack.enter_async_context(client)
|
|
265
|
+
msg = f"Connected to client {i+1}/{len(self.clients)}"
|
|
266
|
+
logger.debug(msg)
|
|
267
|
+
except Exception as e:
|
|
268
|
+
logger.error(f"Failed to connect to client {i+1}: {e}")
|
|
269
|
+
# AsyncExitStack will cleanup already connected clients
|
|
270
|
+
await self._exit_stack.aclose()
|
|
271
|
+
self._exit_stack = None
|
|
272
|
+
error_msg = f"Failed to connect to client {i+1}: {e}"
|
|
273
|
+
raise MCPConnectionError(error_msg) from e
|
|
274
|
+
|
|
250
275
|
async def disconnect(self):
|
|
251
276
|
r"""Disconnect from all MCP servers."""
|
|
252
277
|
if not self._is_connected:
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
from typing import Any, Dict, List, Optional
|
|
17
|
+
|
|
18
|
+
from camel.toolkits import BaseToolkit, FunctionTool
|
|
19
|
+
|
|
20
|
+
from .mcp_toolkit import MCPToolkit
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MinimaxMCPToolkit(BaseToolkit):
|
|
24
|
+
r"""MinimaxMCPToolkit provides an interface for interacting with
|
|
25
|
+
MiniMax AI services using the MiniMax MCP server.
|
|
26
|
+
|
|
27
|
+
This toolkit enables access to MiniMax's multimedia generation
|
|
28
|
+
capabilities including text-to-audio, voice cloning, video generation,
|
|
29
|
+
image generation, music generation, and voice design.
|
|
30
|
+
|
|
31
|
+
This toolkit can be used as an async context manager for automatic
|
|
32
|
+
connection management:
|
|
33
|
+
|
|
34
|
+
# Using explicit API key
|
|
35
|
+
async with MinimaxMCPToolkit(api_key="your-key") as toolkit:
|
|
36
|
+
tools = toolkit.get_tools()
|
|
37
|
+
# Toolkit is automatically disconnected when exiting
|
|
38
|
+
|
|
39
|
+
# Using environment variables (recommended for security)
|
|
40
|
+
# Set MINIMAX_API_KEY=your-key in environment
|
|
41
|
+
async with MinimaxMCPToolkit() as toolkit:
|
|
42
|
+
tools = toolkit.get_tools()
|
|
43
|
+
|
|
44
|
+
Environment Variables:
|
|
45
|
+
MINIMAX_API_KEY: MiniMax API key for authentication
|
|
46
|
+
MINIMAX_API_HOST: API host URL (default: https://api.minimax.io)
|
|
47
|
+
MINIMAX_MCP_BASE_PATH: Base path for output files
|
|
48
|
+
|
|
49
|
+
Attributes:
|
|
50
|
+
timeout (Optional[float]): Connection timeout in seconds.
|
|
51
|
+
(default: :obj:`None`)
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
api_key: Optional[str] = None,
|
|
57
|
+
api_host: str = "https://api.minimax.io",
|
|
58
|
+
base_path: Optional[str] = None,
|
|
59
|
+
timeout: Optional[float] = None,
|
|
60
|
+
) -> None:
|
|
61
|
+
r"""Initializes the MinimaxMCPToolkit.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
api_key (Optional[str]): MiniMax API key for authentication.
|
|
65
|
+
If None, will attempt to read from MINIMAX_API_KEY
|
|
66
|
+
environment variable. (default: :obj:`None`)
|
|
67
|
+
api_host (str): MiniMax API host URL. Can be either
|
|
68
|
+
"https://api.minimax.io" (global) or
|
|
69
|
+
"https://api.minimaxi.com" (mainland China).
|
|
70
|
+
Can also be read from MINIMAX_API_HOST environment variable.
|
|
71
|
+
(default: :obj:`"https://api.minimax.io"`)
|
|
72
|
+
base_path (Optional[str]): Base path for output files.
|
|
73
|
+
If None, uses current working directory. Can also be read
|
|
74
|
+
from MINIMAX_MCP_BASE_PATH environment variable.
|
|
75
|
+
(default: :obj:`None`)
|
|
76
|
+
timeout (Optional[float]): Connection timeout in seconds.
|
|
77
|
+
(default: :obj:`None`)
|
|
78
|
+
"""
|
|
79
|
+
super().__init__(timeout=timeout)
|
|
80
|
+
|
|
81
|
+
# Read API key from parameter or environment variable
|
|
82
|
+
if api_key is None:
|
|
83
|
+
api_key = os.getenv("MINIMAX_API_KEY")
|
|
84
|
+
|
|
85
|
+
if not api_key:
|
|
86
|
+
raise ValueError(
|
|
87
|
+
"api_key must be provided either as a parameter or through "
|
|
88
|
+
"the MINIMAX_API_KEY environment variable"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Read API host from environment variable if not overridden
|
|
92
|
+
env_api_host = os.getenv("MINIMAX_API_HOST")
|
|
93
|
+
if env_api_host:
|
|
94
|
+
api_host = env_api_host
|
|
95
|
+
|
|
96
|
+
# Read base path from environment variable if not provided
|
|
97
|
+
if base_path is None:
|
|
98
|
+
base_path = os.getenv("MINIMAX_MCP_BASE_PATH")
|
|
99
|
+
|
|
100
|
+
# Set up environment variables for the MCP server
|
|
101
|
+
env = {
|
|
102
|
+
"MINIMAX_API_KEY": api_key,
|
|
103
|
+
"MINIMAX_API_HOST": api_host,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if base_path:
|
|
107
|
+
env["MINIMAX_MCP_BASE_PATH"] = base_path
|
|
108
|
+
|
|
109
|
+
self._mcp_toolkit = MCPToolkit(
|
|
110
|
+
config_dict={
|
|
111
|
+
"mcpServers": {
|
|
112
|
+
"minimax": {
|
|
113
|
+
"command": "uvx",
|
|
114
|
+
"args": ["minimax-mcp", "-y"],
|
|
115
|
+
"env": env,
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
timeout=timeout,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
async def connect(self):
|
|
123
|
+
r"""Explicitly connect to the MiniMax MCP server."""
|
|
124
|
+
await self._mcp_toolkit.connect()
|
|
125
|
+
|
|
126
|
+
async def disconnect(self):
|
|
127
|
+
r"""Explicitly disconnect from the MiniMax MCP server."""
|
|
128
|
+
await self._mcp_toolkit.disconnect()
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def is_connected(self) -> bool:
|
|
132
|
+
r"""Check if the toolkit is connected to the MCP server.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
bool: True if connected, False otherwise.
|
|
136
|
+
"""
|
|
137
|
+
return self._mcp_toolkit.is_connected
|
|
138
|
+
|
|
139
|
+
async def __aenter__(self) -> "MinimaxMCPToolkit":
|
|
140
|
+
r"""Async context manager entry point.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
MinimaxMCPToolkit: The connected toolkit instance.
|
|
144
|
+
|
|
145
|
+
Example:
|
|
146
|
+
async with MinimaxMCPToolkit(api_key="your-key") as toolkit:
|
|
147
|
+
tools = toolkit.get_tools()
|
|
148
|
+
"""
|
|
149
|
+
await self.connect()
|
|
150
|
+
return self
|
|
151
|
+
|
|
152
|
+
async def __aexit__(self, _exc_type, _exc_val, _exc_tb) -> None:
|
|
153
|
+
r"""Async context manager exit point.
|
|
154
|
+
|
|
155
|
+
Automatically disconnects from the MiniMax MCP server.
|
|
156
|
+
"""
|
|
157
|
+
await self.disconnect()
|
|
158
|
+
|
|
159
|
+
def get_tools(self) -> List[FunctionTool]:
|
|
160
|
+
r"""Returns a list of tools provided by the MiniMax MCP server.
|
|
161
|
+
|
|
162
|
+
This includes tools for:
|
|
163
|
+
- Text-to-audio conversion
|
|
164
|
+
- Voice cloning
|
|
165
|
+
- Video generation
|
|
166
|
+
- Image generation
|
|
167
|
+
- Music generation
|
|
168
|
+
- Voice design
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
List[FunctionTool]: List of available MiniMax AI tools.
|
|
172
|
+
"""
|
|
173
|
+
return self._mcp_toolkit.get_tools()
|
|
174
|
+
|
|
175
|
+
def get_text_tools(self) -> str:
|
|
176
|
+
r"""Returns a string containing the descriptions of the tools.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
str: A string containing the descriptions of all MiniMax tools.
|
|
180
|
+
"""
|
|
181
|
+
return self._mcp_toolkit.get_text_tools()
|
|
182
|
+
|
|
183
|
+
async def call_tool(
|
|
184
|
+
self, tool_name: str, tool_args: Dict[str, Any]
|
|
185
|
+
) -> Any:
|
|
186
|
+
r"""Call a MiniMax tool by name.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
tool_name (str): Name of the tool to call.
|
|
190
|
+
tool_args (Dict[str, Any]): Arguments to pass to the tool.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Any: The result of the tool call.
|
|
194
|
+
"""
|
|
195
|
+
return await self._mcp_toolkit.call_tool(tool_name, tool_args)
|
camel/toolkits/search_toolkit.py
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
import os
|
|
15
|
+
import warnings
|
|
15
16
|
from typing import Any, Dict, List, Literal, Optional, TypeAlias, Union, cast
|
|
16
17
|
|
|
17
18
|
import requests
|
|
@@ -683,7 +684,7 @@ class SearchToolkit(BaseToolkit):
|
|
|
683
684
|
responses.append({"error": f"google search failed: {e!s}"})
|
|
684
685
|
return responses
|
|
685
686
|
|
|
686
|
-
def
|
|
687
|
+
def search_tavily(
|
|
687
688
|
self, query: str, number_of_result_pages: int = 10, **kwargs
|
|
688
689
|
) -> List[Dict[str, Any]]:
|
|
689
690
|
r"""Use Tavily Search API to search information for the given query.
|
|
@@ -1358,7 +1359,7 @@ class SearchToolkit(BaseToolkit):
|
|
|
1358
1359
|
FunctionTool(self.search_linkup),
|
|
1359
1360
|
FunctionTool(self.search_google),
|
|
1360
1361
|
FunctionTool(self.search_duckduckgo),
|
|
1361
|
-
FunctionTool(self.
|
|
1362
|
+
FunctionTool(self.search_tavily),
|
|
1362
1363
|
FunctionTool(self.search_brave),
|
|
1363
1364
|
FunctionTool(self.search_bocha),
|
|
1364
1365
|
FunctionTool(self.search_baidu),
|
|
@@ -1367,3 +1368,13 @@ class SearchToolkit(BaseToolkit):
|
|
|
1367
1368
|
FunctionTool(self.search_alibaba_tongxiao),
|
|
1368
1369
|
FunctionTool(self.search_metaso),
|
|
1369
1370
|
]
|
|
1371
|
+
|
|
1372
|
+
# Deprecated method alias for backward compatibility
|
|
1373
|
+
def tavily_search(self, *args, **kwargs):
|
|
1374
|
+
r"""Deprecated: Use search_tavily instead for consistency with other search methods."""
|
|
1375
|
+
warnings.warn(
|
|
1376
|
+
"tavily_search is deprecated. Use search_tavily instead for consistency.",
|
|
1377
|
+
DeprecationWarning,
|
|
1378
|
+
stacklevel=2,
|
|
1379
|
+
)
|
|
1380
|
+
return self.search_tavily(*args, **kwargs)
|
|
@@ -66,6 +66,9 @@ class TerminalToolkit(BaseToolkit):
|
|
|
66
66
|
connecting them to the terminal's standard input. This is useful
|
|
67
67
|
for commands that require user input, like `ssh`. Interactive mode
|
|
68
68
|
is only supported on macOS and Linux. (default: :obj:`False`)
|
|
69
|
+
log_dir (Optional[str]): Custom directory path for log files.
|
|
70
|
+
If None, logs are saved to the current working directory.
|
|
71
|
+
(default: :obj:`None`)
|
|
69
72
|
|
|
70
73
|
Note:
|
|
71
74
|
Most functions are compatible with Unix-based systems (macOS, Linux).
|
|
@@ -83,6 +86,7 @@ class TerminalToolkit(BaseToolkit):
|
|
|
83
86
|
clone_current_env: bool = False,
|
|
84
87
|
safe_mode: bool = True,
|
|
85
88
|
interactive: bool = False,
|
|
89
|
+
log_dir: Optional[str] = None,
|
|
86
90
|
):
|
|
87
91
|
# Store timeout before calling super().__init__
|
|
88
92
|
self._timeout = timeout
|
|
@@ -99,6 +103,7 @@ class TerminalToolkit(BaseToolkit):
|
|
|
99
103
|
self.use_shell_mode = use_shell_mode
|
|
100
104
|
self._human_takeover_active = False
|
|
101
105
|
self.interactive = interactive
|
|
106
|
+
self.log_dir = log_dir
|
|
102
107
|
|
|
103
108
|
self.python_executable = sys.executable
|
|
104
109
|
self.is_macos = platform.system() == 'Darwin'
|
|
@@ -153,8 +158,13 @@ class TerminalToolkit(BaseToolkit):
|
|
|
153
158
|
r"""Set up file output to replace GUI, using a fixed file to simulate
|
|
154
159
|
terminal.
|
|
155
160
|
"""
|
|
156
|
-
|
|
157
|
-
self.
|
|
161
|
+
# Use custom log directory if provided, otherwise use current directory
|
|
162
|
+
if self.log_dir:
|
|
163
|
+
# Create the log directory if it doesn't exist
|
|
164
|
+
os.makedirs(self.log_dir, exist_ok=True)
|
|
165
|
+
self.log_file = os.path.join(self.log_dir, "camel_terminal.txt")
|
|
166
|
+
else:
|
|
167
|
+
self.log_file = os.path.join(os.getcwd(), "camel_terminal.txt")
|
|
158
168
|
|
|
159
169
|
# Inform the user
|
|
160
170
|
logger.info(f"Terminal output will be redirected to: {self.log_file}")
|
|
@@ -195,18 +195,24 @@ class VideoAnalysisToolkit(BaseToolkit):
|
|
|
195
195
|
destroyed.
|
|
196
196
|
"""
|
|
197
197
|
# Clean up temporary files
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
198
|
+
if hasattr(self, '_temp_files'):
|
|
199
|
+
for temp_file in self._temp_files:
|
|
200
|
+
if os.path.exists(temp_file):
|
|
201
|
+
try:
|
|
202
|
+
os.remove(temp_file)
|
|
203
|
+
logger.debug(f"Removed temporary file: {temp_file}")
|
|
204
|
+
except OSError as e:
|
|
205
|
+
logger.warning(
|
|
206
|
+
f"Failed to remove temporary file {temp_file}: {e}"
|
|
207
|
+
)
|
|
207
208
|
|
|
208
209
|
# Clean up temporary directory if needed
|
|
209
|
-
if
|
|
210
|
+
if (
|
|
211
|
+
hasattr(self, '_cleanup')
|
|
212
|
+
and self._cleanup
|
|
213
|
+
and hasattr(self, '_working_directory')
|
|
214
|
+
and os.path.exists(self._working_directory)
|
|
215
|
+
):
|
|
210
216
|
try:
|
|
211
217
|
import sys
|
|
212
218
|
|
camel/types/enums.py
CHANGED
|
@@ -64,6 +64,8 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
64
64
|
AWS_CLAUDE_OPUS_4 = "anthropic.claude-opus-4-20250514-v1:0"
|
|
65
65
|
AWS_CLAUDE_OPUS_4_1 = "anthropic.claude-opus-4-1-20250805-v1:0"
|
|
66
66
|
|
|
67
|
+
AMD_GPT4 = "dvue-aoai-001-gpt-4.1"
|
|
68
|
+
|
|
67
69
|
GLM_4 = "glm-4"
|
|
68
70
|
GLM_4V = "glm-4v"
|
|
69
71
|
GLM_4V_FLASH = "glm-4v-flash"
|
|
@@ -87,6 +89,15 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
87
89
|
GROQ_MIXTRAL_8_7B = "mixtral-8x7b-32768"
|
|
88
90
|
GROQ_GEMMA_2_9B_IT = "gemma2-9b-it"
|
|
89
91
|
|
|
92
|
+
# Nebius AI Studio platform models
|
|
93
|
+
NEBIUS_GPT_OSS_120B = "gpt-oss-120b"
|
|
94
|
+
NEBIUS_GPT_OSS_20B = "gpt-oss-20b"
|
|
95
|
+
NEBIUS_GLM_4_5 = "GLM-4.5"
|
|
96
|
+
NEBIUS_DEEPSEEK_V3 = "deepseek-ai/DeepSeek-V3"
|
|
97
|
+
NEBIUS_DEEPSEEK_R1 = "deepseek-ai/DeepSeek-R1"
|
|
98
|
+
NEBIUS_LLAMA_3_1_70B = "meta-llama/Meta-Llama-3.1-70B-Instruct"
|
|
99
|
+
NEBIUS_MISTRAL_7B_INSTRUCT = "mistralai/Mistral-7B-Instruct-v0.3"
|
|
100
|
+
|
|
90
101
|
# OpenRouter models
|
|
91
102
|
OPENROUTER_LLAMA_3_1_405B = "meta-llama/llama-3.1-405b-instruct"
|
|
92
103
|
OPENROUTER_LLAMA_3_1_70B = "meta-llama/llama-3.1-70b-instruct"
|
|
@@ -507,6 +518,13 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
507
518
|
ModelType.O3,
|
|
508
519
|
}
|
|
509
520
|
|
|
521
|
+
@property
|
|
522
|
+
def is_amd(self) -> bool:
|
|
523
|
+
r"""Returns whether this type of models is a AMD model."""
|
|
524
|
+
return self in {
|
|
525
|
+
ModelType.AMD_GPT4,
|
|
526
|
+
}
|
|
527
|
+
|
|
510
528
|
@property
|
|
511
529
|
def is_aws_bedrock(self) -> bool:
|
|
512
530
|
r"""Returns whether this type of models is an AWS Bedrock model."""
|
|
@@ -603,6 +621,20 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
603
621
|
ModelType.GROQ_GEMMA_2_9B_IT,
|
|
604
622
|
}
|
|
605
623
|
|
|
624
|
+
@property
|
|
625
|
+
def is_nebius(self) -> bool:
|
|
626
|
+
r"""Returns whether this type of models is served by Nebius AI
|
|
627
|
+
Studio."""
|
|
628
|
+
return self in {
|
|
629
|
+
ModelType.NEBIUS_GPT_OSS_120B,
|
|
630
|
+
ModelType.NEBIUS_GPT_OSS_20B,
|
|
631
|
+
ModelType.NEBIUS_GLM_4_5,
|
|
632
|
+
ModelType.NEBIUS_DEEPSEEK_V3,
|
|
633
|
+
ModelType.NEBIUS_DEEPSEEK_R1,
|
|
634
|
+
ModelType.NEBIUS_LLAMA_3_1_70B,
|
|
635
|
+
ModelType.NEBIUS_MISTRAL_7B_INSTRUCT,
|
|
636
|
+
}
|
|
637
|
+
|
|
606
638
|
@property
|
|
607
639
|
def is_openrouter(self) -> bool:
|
|
608
640
|
r"""Returns whether this type of models is served by OpenRouter."""
|
|
@@ -1138,6 +1170,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
1138
1170
|
ModelType.NOVITA_MISTRAL_7B,
|
|
1139
1171
|
ModelType.NOVITA_LLAMA_3_2_11B_VISION,
|
|
1140
1172
|
ModelType.NOVITA_LLAMA_3_2_3B,
|
|
1173
|
+
ModelType.NEBIUS_MISTRAL_7B_INSTRUCT,
|
|
1141
1174
|
}:
|
|
1142
1175
|
return 32_768
|
|
1143
1176
|
elif self in {
|
|
@@ -1227,6 +1260,11 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
1227
1260
|
ModelType.ERNIE_4_5_TURBO_128K,
|
|
1228
1261
|
ModelType.DEEPSEEK_V3,
|
|
1229
1262
|
ModelType.MOONSHOT_KIMI_K2,
|
|
1263
|
+
ModelType.NEBIUS_GLM_4_5,
|
|
1264
|
+
ModelType.NEBIUS_DEEPSEEK_V3,
|
|
1265
|
+
ModelType.NEBIUS_DEEPSEEK_R1,
|
|
1266
|
+
ModelType.NEBIUS_GPT_OSS_120B,
|
|
1267
|
+
ModelType.NEBIUS_GPT_OSS_20B,
|
|
1230
1268
|
}:
|
|
1231
1269
|
return 128_000
|
|
1232
1270
|
elif self in {
|
|
@@ -1261,6 +1299,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
1261
1299
|
ModelType.NOVITA_LLAMA_4_SCOUT_17B,
|
|
1262
1300
|
ModelType.NOVITA_LLAMA_3_3_70B,
|
|
1263
1301
|
ModelType.NOVITA_MISTRAL_NEMO,
|
|
1302
|
+
ModelType.NEBIUS_LLAMA_3_1_70B,
|
|
1264
1303
|
}:
|
|
1265
1304
|
return 131_072
|
|
1266
1305
|
elif self in {
|
|
@@ -1319,6 +1358,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
1319
1358
|
ModelType.GLM_4_LONG,
|
|
1320
1359
|
ModelType.TOGETHER_LLAMA_4_MAVERICK,
|
|
1321
1360
|
ModelType.OPENROUTER_LLAMA_4_MAVERICK,
|
|
1361
|
+
ModelType.AMD_GPT4,
|
|
1322
1362
|
ModelType.GPT_4_1,
|
|
1323
1363
|
ModelType.GPT_4_1_MINI,
|
|
1324
1364
|
ModelType.GPT_4_1_NANO,
|
|
@@ -1532,6 +1572,7 @@ class ModelPlatformType(Enum):
|
|
|
1532
1572
|
AZURE = "azure"
|
|
1533
1573
|
ANTHROPIC = "anthropic"
|
|
1534
1574
|
GROQ = "groq"
|
|
1575
|
+
NEBIUS = "nebius"
|
|
1535
1576
|
OPENROUTER = "openrouter"
|
|
1536
1577
|
OLLAMA = "ollama"
|
|
1537
1578
|
LITELLM = "litellm"
|
|
@@ -1548,6 +1589,7 @@ class ModelPlatformType(Enum):
|
|
|
1548
1589
|
COHERE = "cohere"
|
|
1549
1590
|
YI = "lingyiwanwu"
|
|
1550
1591
|
QWEN = "tongyi-qianwen"
|
|
1592
|
+
AMD = "amd"
|
|
1551
1593
|
NVIDIA = "nvidia"
|
|
1552
1594
|
DEEPSEEK = "deepseek"
|
|
1553
1595
|
PPIO = "ppio"
|
|
@@ -95,6 +95,11 @@ class UnifiedModelType(str):
|
|
|
95
95
|
r"""Returns whether the model is a Groq served model."""
|
|
96
96
|
return True
|
|
97
97
|
|
|
98
|
+
@property
|
|
99
|
+
def is_nebius(self) -> bool:
|
|
100
|
+
r"""Returns whether the model is a Nebius AI Studio served model."""
|
|
101
|
+
return True
|
|
102
|
+
|
|
98
103
|
@property
|
|
99
104
|
def is_openrouter(self) -> bool:
|
|
100
105
|
r"""Returns whether the model is a OpenRouter served model."""
|
camel/utils/commons.py
CHANGED
|
@@ -354,6 +354,8 @@ def api_keys_required(
|
|
|
354
354
|
key_way = "https://www.zhipuai.cn/"
|
|
355
355
|
elif env_var_name == 'KLAVIS_API_KEY':
|
|
356
356
|
key_way = "https://www.klavis.ai/docs"
|
|
357
|
+
elif env_var_name == 'XAI_API_KEY':
|
|
358
|
+
key_way = "https://api.x.ai/v1"
|
|
357
359
|
|
|
358
360
|
if missing_keys:
|
|
359
361
|
raise ValueError(
|