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.

Files changed (47) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +298 -130
  3. camel/configs/__init__.py +6 -0
  4. camel/configs/amd_config.py +70 -0
  5. camel/configs/nebius_config.py +103 -0
  6. camel/interpreters/__init__.py +2 -0
  7. camel/interpreters/microsandbox_interpreter.py +395 -0
  8. camel/models/__init__.py +4 -0
  9. camel/models/amd_model.py +101 -0
  10. camel/models/model_factory.py +4 -0
  11. camel/models/nebius_model.py +83 -0
  12. camel/models/ollama_model.py +3 -3
  13. camel/models/openai_model.py +0 -6
  14. camel/runtimes/daytona_runtime.py +11 -12
  15. camel/societies/workforce/task_channel.py +120 -27
  16. camel/societies/workforce/workforce.py +35 -3
  17. camel/toolkits/__init__.py +5 -3
  18. camel/toolkits/code_execution.py +28 -1
  19. camel/toolkits/function_tool.py +6 -1
  20. camel/toolkits/github_toolkit.py +104 -17
  21. camel/toolkits/hybrid_browser_toolkit/config_loader.py +8 -0
  22. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +12 -0
  23. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +33 -14
  24. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +135 -40
  25. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +2 -0
  26. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +43 -207
  27. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  28. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +231 -0
  29. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  30. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +39 -6
  31. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +248 -58
  32. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +5 -1
  33. camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +98 -31
  34. camel/toolkits/math_toolkit.py +64 -10
  35. camel/toolkits/mcp_toolkit.py +39 -14
  36. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  37. camel/toolkits/search_toolkit.py +13 -2
  38. camel/toolkits/terminal_toolkit.py +12 -2
  39. camel/toolkits/video_analysis_toolkit.py +16 -10
  40. camel/types/enums.py +42 -0
  41. camel/types/unified_model_type.py +5 -0
  42. camel/utils/commons.py +2 -0
  43. camel/utils/mcp.py +136 -2
  44. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/METADATA +5 -11
  45. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/RECORD +47 -38
  46. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/WHEEL +0 -0
  47. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/licenses/LICENSE +0 -0
@@ -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 add(self, a: float, b: float) -> float:
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 sub(self, a: float, b: float) -> float:
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 multiply(self, a: float, b: float, decimal_places: int = 2) -> float:
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 divide(self, a: float, b: float, decimal_places: int = 2) -> float:
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 round(self, a: float, decimal_places: int = 0) -> float:
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.add),
105
- FunctionTool(self.sub),
106
- FunctionTool(self.multiply),
107
- FunctionTool(self.divide),
108
- FunctionTool(self.round),
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)
@@ -220,26 +220,34 @@ class MCPToolkit(BaseToolkit):
220
220
  self._exit_stack = AsyncExitStack()
221
221
 
222
222
  try:
223
- # Connect to all clients using AsyncExitStack
224
- for i, client in enumerate(self.clients):
225
- try:
226
- # Use MCPClient directly as async context manager
227
- await self._exit_stack.enter_async_context(client)
228
- msg = f"Connected to client {i+1}/{len(self.clients)}"
229
- logger.debug(msg)
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)
@@ -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 tavily_search(
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.tavily_search),
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.log_file = os.path.join(os.getcwd(), "camel_terminal.txt")
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
- for temp_file in self._temp_files:
199
- if os.path.exists(temp_file):
200
- try:
201
- os.remove(temp_file)
202
- logger.debug(f"Removed temporary file: {temp_file}")
203
- except OSError as e:
204
- logger.warning(
205
- f"Failed to remove temporary file {temp_file}: {e}"
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 self._cleanup and os.path.exists(self._working_directory):
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(