camel-ai 0.2.54__py3-none-any.whl → 0.2.56__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 CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  from camel.logger import disable_logging, enable_logging, set_log_level
16
16
 
17
- __version__ = '0.2.54'
17
+ __version__ = '0.2.56'
18
18
 
19
19
  __all__ = [
20
20
  '__version__',
@@ -752,7 +752,7 @@ class ChatAgent(BaseAgent):
752
752
  try:
753
753
  openai_messages, num_tokens = self.memory.get_context()
754
754
  except RuntimeError as e:
755
- return self._step_token_exceed(
755
+ return self._step_terminate(
756
756
  e.args[1], tool_call_records, "max_tokens_exceeded"
757
757
  )
758
758
  # Get response from model backend
@@ -765,8 +765,8 @@ class ChatAgent(BaseAgent):
765
765
 
766
766
  # Terminate Agent if stop_event is set
767
767
  if self.stop_event and self.stop_event.is_set():
768
- # Use the _step_token_exceed to terminate the agent with reason
769
- return self._step_token_exceed(
768
+ # Use the _step_terminate to terminate the agent with reason
769
+ return self._step_terminate(
770
770
  num_tokens, tool_call_records, "termination_triggered"
771
771
  )
772
772
 
@@ -851,7 +851,7 @@ class ChatAgent(BaseAgent):
851
851
  try:
852
852
  openai_messages, num_tokens = self.memory.get_context()
853
853
  except RuntimeError as e:
854
- return self._step_token_exceed(
854
+ return self._step_terminate(
855
855
  e.args[1], tool_call_records, "max_tokens_exceeded"
856
856
  )
857
857
 
@@ -864,8 +864,8 @@ class ChatAgent(BaseAgent):
864
864
 
865
865
  # Terminate Agent if stop_event is set
866
866
  if self.stop_event and self.stop_event.is_set():
867
- # Use the _step_token_exceed to terminate the agent with reason
868
- return self._step_token_exceed(
867
+ # Use the _step_terminate to terminate the agent with reason
868
+ return self._step_terminate(
869
869
  num_tokens, tool_call_records, "termination_triggered"
870
870
  )
871
871
 
@@ -1396,24 +1396,30 @@ class ChatAgent(BaseAgent):
1396
1396
  )
1397
1397
  output_messages.append(chat_message)
1398
1398
 
1399
- def _step_token_exceed(
1399
+ def _step_terminate(
1400
1400
  self,
1401
1401
  num_tokens: int,
1402
1402
  tool_calls: List[ToolCallingRecord],
1403
1403
  termination_reason: str,
1404
1404
  ) -> ChatAgentResponse:
1405
- r"""Return trivial response containing number of tokens and information
1406
- of called functions when the number of tokens exceeds.
1405
+ r"""Create a response when the agent execution is terminated.
1406
+
1407
+ This method is called when the agent needs to terminate its execution
1408
+ due to various reasons such as token limit exceeded, or other
1409
+ termination conditions. It creates a response with empty messages but
1410
+ includes termination information in the info dictionary.
1407
1411
 
1408
1412
  Args:
1409
1413
  num_tokens (int): Number of tokens in the messages.
1410
1414
  tool_calls (List[ToolCallingRecord]): List of information
1411
1415
  objects of functions called in the current step.
1412
- termination_reason (str): String of termination reason.
1416
+ termination_reason (str): String describing the reason for
1417
+ termination.
1413
1418
 
1414
1419
  Returns:
1415
- ChatAgentResponse: The struct containing trivial outputs and
1416
- information about token number and called functions.
1420
+ ChatAgentResponse: A response object with empty message list,
1421
+ terminated flag set to True, and an info dictionary containing
1422
+ termination details, token counts, and tool call information.
1417
1423
  """
1418
1424
  self.terminated = True
1419
1425
 
@@ -25,6 +25,7 @@ if TYPE_CHECKING:
25
25
  from openai import AsyncStream
26
26
 
27
27
  from camel.configs import MISTRAL_API_PARAMS, MistralConfig
28
+ from camel.logger import get_logger
28
29
  from camel.messages import OpenAIMessage
29
30
  from camel.models import BaseModelBackend
30
31
  from camel.models._utils import try_modify_message_with_format
@@ -36,6 +37,8 @@ from camel.utils import (
36
37
  dependencies_required,
37
38
  )
38
39
 
40
+ logger = get_logger(__name__)
41
+
39
42
  try:
40
43
  if os.getenv("AGENTOPS_API_KEY") is not None:
41
44
  from agentops import LLMEvent, record
@@ -235,7 +238,38 @@ class MistralModel(BaseModelBackend):
235
238
  response_format: Optional[Type[BaseModel]] = None,
236
239
  tools: Optional[List[Dict[str, Any]]] = None,
237
240
  ) -> Union[ChatCompletion, AsyncStream[ChatCompletionChunk]]:
238
- raise NotImplementedError("Mistral does not support async inference.")
241
+ logger.warning(
242
+ "Mistral does not support async inference, using sync "
243
+ "inference instead."
244
+ )
245
+ request_config = self._prepare_request(
246
+ messages, response_format, tools
247
+ )
248
+ mistral_messages = self._to_mistral_chatmessage(messages)
249
+
250
+ response = self._client.chat.complete(
251
+ messages=mistral_messages,
252
+ model=self.model_type,
253
+ **request_config,
254
+ )
255
+
256
+ openai_response = self._to_openai_response(response) # type: ignore[arg-type]
257
+
258
+ # Add AgentOps LLM Event tracking
259
+ if LLMEvent:
260
+ llm_event = LLMEvent(
261
+ thread_id=openai_response.id,
262
+ prompt=" ".join(
263
+ [message.get("content") for message in messages] # type: ignore[misc]
264
+ ),
265
+ prompt_tokens=openai_response.usage.prompt_tokens, # type: ignore[union-attr]
266
+ completion=openai_response.choices[0].message.content,
267
+ completion_tokens=openai_response.usage.completion_tokens, # type: ignore[union-attr]
268
+ model=self.model_type,
269
+ )
270
+ record(llm_event)
271
+
272
+ return openai_response
239
273
 
240
274
  def _run(
241
275
  self,
@@ -12,7 +12,7 @@
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  import json
15
- from typing import Dict, Optional, Type, Union
15
+ from typing import ClassVar, Dict, Optional, Type, Union
16
16
 
17
17
  import yaml
18
18
 
@@ -62,6 +62,44 @@ class ModelFactory:
62
62
  ValueError: in case the provided model type is unknown.
63
63
  """
64
64
 
65
+ _MODEL_PLATFORM_TO_CLASS_MAP: ClassVar[
66
+ Dict[ModelPlatformType, Type[BaseModelBackend]]
67
+ ] = {
68
+ ModelPlatformType.OLLAMA: OllamaModel,
69
+ ModelPlatformType.VLLM: VLLMModel,
70
+ ModelPlatformType.SGLANG: SGLangModel,
71
+ ModelPlatformType.OPENAI_COMPATIBLE_MODEL: OpenAICompatibleModel,
72
+ ModelPlatformType.SAMBA: SambaModel,
73
+ ModelPlatformType.TOGETHER: TogetherAIModel,
74
+ ModelPlatformType.LITELLM: LiteLLMModel,
75
+ ModelPlatformType.AWS_BEDROCK: AWSBedrockModel,
76
+ ModelPlatformType.NVIDIA: NvidiaModel,
77
+ ModelPlatformType.SILICONFLOW: SiliconFlowModel,
78
+ ModelPlatformType.AIML: AIMLModel,
79
+ ModelPlatformType.VOLCANO: VolcanoModel,
80
+ ModelPlatformType.NETMIND: NetmindModel,
81
+ ModelPlatformType.OPENAI: OpenAIModel,
82
+ ModelPlatformType.AZURE: AzureOpenAIModel,
83
+ ModelPlatformType.ANTHROPIC: AnthropicModel,
84
+ ModelPlatformType.GROQ: GroqModel,
85
+ ModelPlatformType.LMSTUDIO: LMStudioModel,
86
+ ModelPlatformType.OPENROUTER: OpenRouterModel,
87
+ ModelPlatformType.ZHIPU: ZhipuAIModel,
88
+ ModelPlatformType.GEMINI: GeminiModel,
89
+ ModelPlatformType.MISTRAL: MistralModel,
90
+ ModelPlatformType.REKA: RekaModel,
91
+ ModelPlatformType.COHERE: CohereModel,
92
+ ModelPlatformType.YI: YiModel,
93
+ ModelPlatformType.QWEN: QwenModel,
94
+ ModelPlatformType.DEEPSEEK: DeepSeekModel,
95
+ ModelPlatformType.PPIO: PPIOModel,
96
+ ModelPlatformType.INTERNLM: InternLMModel,
97
+ ModelPlatformType.MOONSHOT: MoonshotModel,
98
+ ModelPlatformType.MODELSCOPE: ModelScopeModel,
99
+ ModelPlatformType.NOVITA: NovitaModel,
100
+ ModelPlatformType.WATSONX: WatsonXModel,
101
+ }
102
+
65
103
  @staticmethod
66
104
  def create(
67
105
  model_platform: Union[ModelPlatformType, str],
@@ -124,81 +162,15 @@ class ModelFactory:
124
162
  model_class: Optional[Type[BaseModelBackend]] = None
125
163
  model_type = UnifiedModelType(model_type)
126
164
 
127
- if model_platform.is_ollama:
128
- model_class = OllamaModel
129
- elif model_platform.is_vllm:
130
- model_class = VLLMModel
131
- elif model_platform.is_sglang:
132
- model_class = SGLangModel
133
- elif model_platform.is_openai_compatible_model:
134
- model_class = OpenAICompatibleModel
135
- elif model_platform.is_samba:
136
- model_class = SambaModel
137
- elif model_platform.is_together:
138
- model_class = TogetherAIModel
139
- elif model_platform.is_litellm:
140
- model_class = LiteLLMModel
141
- elif model_platform.is_aws_bedrock:
142
- model_class = AWSBedrockModel
143
- elif model_platform.is_nvidia:
144
- model_class = NvidiaModel
145
- elif model_platform.is_siliconflow:
146
- model_class = SiliconFlowModel
147
- elif model_platform.is_aiml:
148
- model_class = AIMLModel
149
- elif model_platform.is_volcano:
150
- model_class = VolcanoModel
151
- elif model_platform.is_netmind:
152
- model_class = NetmindModel
153
-
154
- elif model_platform.is_openai and model_type.is_openai:
155
- model_class = OpenAIModel
156
- elif model_platform.is_azure and model_type.is_azure_openai:
157
- model_class = AzureOpenAIModel
158
- elif model_platform.is_anthropic and model_type.is_anthropic:
159
- model_class = AnthropicModel
160
- elif model_platform.is_groq and model_type.is_groq:
161
- model_class = GroqModel
162
- elif model_platform.is_lmstudio and model_type.is_lmstudio:
163
- model_class = LMStudioModel
164
- elif model_platform.is_openrouter and model_type.is_openrouter:
165
- model_class = OpenRouterModel
166
- elif model_platform.is_zhipuai and model_type.is_zhipuai:
167
- model_class = ZhipuAIModel
168
- elif model_platform.is_gemini and model_type.is_gemini:
169
- model_class = GeminiModel
170
- elif model_platform.is_mistral and model_type.is_mistral:
171
- model_class = MistralModel
172
- elif model_platform.is_reka and model_type.is_reka:
173
- model_class = RekaModel
174
- elif model_platform.is_cohere and model_type.is_cohere:
175
- model_class = CohereModel
176
- elif model_platform.is_yi and model_type.is_yi:
177
- model_class = YiModel
178
- elif model_platform.is_qwen and model_type.is_qwen:
179
- model_class = QwenModel
180
- elif model_platform.is_deepseek:
181
- model_class = DeepSeekModel
182
- elif model_platform.is_ppio:
183
- model_class = PPIOModel
184
- elif model_platform.is_internlm and model_type.is_internlm:
185
- model_class = InternLMModel
186
- elif model_platform.is_moonshot and model_type.is_moonshot:
187
- model_class = MoonshotModel
188
- elif model_platform.is_modelscope:
189
- model_class = ModelScopeModel
190
- elif model_platform.is_novita:
191
- model_class = NovitaModel
192
- elif model_type == ModelType.STUB:
165
+ model_class = ModelFactory._MODEL_PLATFORM_TO_CLASS_MAP.get(
166
+ model_platform
167
+ )
168
+
169
+ if model_type == ModelType.STUB:
193
170
  model_class = StubModel
194
- elif model_type.is_watsonx:
195
- model_class = WatsonXModel
196
171
 
197
172
  if model_class is None:
198
- raise ValueError(
199
- f"Unknown pair of model platform `{model_platform}` "
200
- f"and model type `{model_type}`."
201
- )
173
+ raise ValueError(f"Unknown model platform `{model_platform}`")
202
174
 
203
175
  return model_class(
204
176
  model_type=model_type,
@@ -68,8 +68,10 @@ from .pyautogui_toolkit import PyAutoGUIToolkit
68
68
  from .openai_agent_toolkit import OpenAIAgentToolkit
69
69
  from .searxng_toolkit import SearxNGToolkit
70
70
  from .jina_reranker_toolkit import JinaRerankerToolkit
71
+ from .pulse_mcp_search_toolkit import PulseMCPSearchToolkit
71
72
  from .klavis_toolkit import KlavisToolkit
72
73
  from .aci_toolkit import ACIToolkit
74
+ from .playwright_mcp_toolkit import PlaywrightMCPToolkit
73
75
 
74
76
 
75
77
  __all__ = [
@@ -126,6 +128,8 @@ __all__ = [
126
128
  'OpenAIAgentToolkit',
127
129
  'SearxNGToolkit',
128
130
  'JinaRerankerToolkit',
131
+ 'PulseMCPSearchToolkit',
129
132
  'KlavisToolkit',
130
133
  'ACIToolkit',
134
+ 'PlaywrightMCPToolkit',
131
135
  ]
@@ -13,6 +13,7 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
 
15
15
  import os
16
+ import urllib.parse
16
17
  from typing import Any, Dict, List, Optional
17
18
 
18
19
  import requests
@@ -169,18 +170,6 @@ class KlavisToolkit(BaseToolkit):
169
170
  endpoint = f"/mcp-server/instance/delete/{instance_id}"
170
171
  return self._request('DELETE', endpoint)
171
172
 
172
- def get_server_tools(self, server_name: str) -> Dict[str, Any]:
173
- r"""Get list of tool names for a specific MCP server.
174
-
175
- Args:
176
- server_name (str): The name of the target MCP server.
177
-
178
- Returns:
179
- Dict[str, Any]: List of tools available for the specified server.
180
- """
181
- endpoint = f"/mcp-server/tools/{server_name}"
182
- return self._request('GET', endpoint)
183
-
184
173
  def get_all_servers(self) -> Dict[str, Any]:
185
174
  r"""Get all MCP servers with their basic information.
186
175
 
@@ -209,6 +198,60 @@ class KlavisToolkit(BaseToolkit):
209
198
  'POST', endpoint, payload=payload, additional_headers=headers
210
199
  )
211
200
 
201
+ def list_tools(self, server_url: str) -> Dict[str, Any]:
202
+ r"""Lists all tools available for a specific remote MCP server.
203
+
204
+ Args:
205
+ server_url (str): The full URL for connecting to the MCP server
206
+ via Server-Sent Events (SSE).
207
+
208
+ Returns:
209
+ Dict[str, Any]: Response containing the list of tools or an error.
210
+ """
211
+
212
+ encoded_server_url = urllib.parse.quote(server_url, safe='')
213
+ endpoint = f"/mcp-server/list-tools/{encoded_server_url}"
214
+ return self._request('GET', endpoint)
215
+
216
+ def call_tool(
217
+ self,
218
+ server_url: str,
219
+ tool_name: str,
220
+ tool_args: Optional[Dict[str, Any]] = None,
221
+ ) -> Dict[str, Any]:
222
+ r"""Calls a remote MCP server tool directly using the provided server
223
+ URL.
224
+
225
+ Args:
226
+ server_url (str): The full URL for connecting to the MCP server
227
+ via Server-Sent Events (SSE).
228
+ tool_name (str): The name of the tool to call.
229
+ tool_args (Optional[Dict[str, Any]]): The input parameters for
230
+ the tool. Defaults to None, which might be treated as empty
231
+ args by the server. (default: :obj:`None`)
232
+
233
+ Returns:
234
+ Dict[str, Any]: Response containing the result of the tool call
235
+ or an error.
236
+ """
237
+ endpoint = "/mcp-server/call-tool"
238
+ payload: Dict[str, Any] = {
239
+ "serverUrl": server_url,
240
+ "toolName": tool_name,
241
+ }
242
+ # Add toolArgs only if provided, otherwise server might expect empty
243
+ # dict
244
+ if tool_args is not None:
245
+ payload["toolArgs"] = tool_args
246
+ else:
247
+ # Explicitly setting to empty dict based on schema interpretation
248
+ payload["toolArgs"] = {}
249
+
250
+ headers = {'Content-Type': 'application/json'}
251
+ return self._request(
252
+ 'POST', endpoint, payload=payload, additional_headers=headers
253
+ )
254
+
212
255
  def get_tools(self) -> List[FunctionTool]:
213
256
  r"""Returns a list of FunctionTool objects representing the functions
214
257
  in the toolkit.
@@ -222,7 +265,8 @@ class KlavisToolkit(BaseToolkit):
222
265
  FunctionTool(self.get_server_instance),
223
266
  FunctionTool(self.delete_auth_data),
224
267
  FunctionTool(self.delete_server_instance),
225
- FunctionTool(self.get_server_tools),
226
268
  FunctionTool(self.get_all_servers),
227
269
  FunctionTool(self.set_auth_token),
270
+ FunctionTool(self.list_tools),
271
+ FunctionTool(self.call_tool),
228
272
  ]
@@ -16,6 +16,7 @@ import json
16
16
  import os
17
17
  import shlex
18
18
  from contextlib import AsyncExitStack, asynccontextmanager
19
+ from datetime import timedelta
19
20
  from typing import (
20
21
  TYPE_CHECKING,
21
22
  Any,
@@ -52,14 +53,15 @@ class MCPClient(BaseToolkit):
52
53
 
53
54
  Attributes:
54
55
  command_or_url (str): URL for SSE mode or command executable for stdio
55
- mode. (default: :obj:`'None'`)
56
+ mode. (default: :obj:`None`)
56
57
  args (List[str]): List of command-line arguments if stdio mode is used.
57
- (default: :obj:`'None'`)
58
+ (default: :obj:`None`)
58
59
  env (Dict[str, str]): Environment variables for the stdio mode command.
59
- (default: :obj:`'None'`)
60
- timeout (Optional[float]): Connection timeout. (default: :obj:`'None'`)
60
+ (default: :obj:`None`)
61
+ timeout (Optional[float]): Connection timeout.
62
+ (default: :obj:`None`)
61
63
  headers (Dict[str, str]): Headers for the HTTP request.
62
- (default: :obj:`'None'`)
64
+ (default: :obj:`None`)
63
65
  strict (Optional[bool]): Whether to enforce strict mode for the
64
66
  function call. (default: :obj:`False`)
65
67
  """
@@ -111,6 +113,7 @@ class MCPClient(BaseToolkit):
111
113
  sse_client(
112
114
  self.command_or_url,
113
115
  headers=self.headers,
116
+ timeout=self.timeout,
114
117
  )
115
118
  )
116
119
  else:
@@ -138,7 +141,11 @@ class MCPClient(BaseToolkit):
138
141
  )
139
142
 
140
143
  self._session = await self._exit_stack.enter_async_context(
141
- ClientSession(read_stream, write_stream)
144
+ ClientSession(
145
+ read_stream,
146
+ write_stream,
147
+ timedelta(seconds=self.timeout) if self.timeout else None,
148
+ )
142
149
  )
143
150
  await self._session.initialize()
144
151
  list_tools_result = await self.list_mcp_tools()
@@ -399,17 +406,20 @@ class MCPToolkit(BaseToolkit):
399
406
 
400
407
  Args:
401
408
  servers (Optional[List[MCPClient]]): List of MCPClient
402
- instances to manage.
409
+ instances to manage. (default: :obj:`None`)
403
410
  config_path (Optional[str]): Path to a JSON configuration file
404
- defining MCP servers.
411
+ defining MCP servers. (default: :obj:`None`)
412
+ config_dict (Optional[Dict[str, Any]]): Dictionary containing MCP
413
+ server configurations in the same format as the config file.
414
+ (default: :obj:`None`)
405
415
  strict (Optional[bool]): Whether to enforce strict mode for the
406
416
  function call. (default: :obj:`False`)
407
417
 
408
418
  Note:
409
- Either `servers` or `config_path` must be provided. If both are
410
- provided, servers from both sources will be combined.
419
+ Either `servers`, `config_path`, or `config_dict` must be provided.
420
+ If multiple are provided, servers from all sources will be combined.
411
421
 
412
- For web servers in the config file, you can specify authorization
422
+ For web servers in the config, you can specify authorization
413
423
  headers using the "headers" field to connect to protected MCP server
414
424
  endpoints.
415
425
 
@@ -438,14 +448,19 @@ class MCPToolkit(BaseToolkit):
438
448
  self,
439
449
  servers: Optional[List[MCPClient]] = None,
440
450
  config_path: Optional[str] = None,
451
+ config_dict: Optional[Dict[str, Any]] = None,
441
452
  strict: Optional[bool] = False,
442
453
  ):
443
454
  super().__init__()
444
455
 
445
- if servers and config_path:
456
+ sources_provided = sum(
457
+ 1 for src in [servers, config_path, config_dict] if src is not None
458
+ )
459
+ if sources_provided > 1:
446
460
  logger.warning(
447
- "Both servers and config_path are provided. "
448
- "Servers from both sources will be combined."
461
+ "Multiple configuration sources provided "
462
+ f"({sources_provided}). Servers from all sources "
463
+ "will be combined."
449
464
  )
450
465
 
451
466
  self.servers: List[MCPClient] = servers or []
@@ -455,6 +470,9 @@ class MCPToolkit(BaseToolkit):
455
470
  self._load_servers_from_config(config_path, strict)
456
471
  )
457
472
 
473
+ if config_dict:
474
+ self.servers.extend(self._load_servers_from_dict(config_dict))
475
+
458
476
  self._exit_stack = AsyncExitStack()
459
477
  self._connected = False
460
478
 
@@ -484,9 +502,25 @@ class MCPToolkit(BaseToolkit):
484
502
  logger.warning(f"Config file not found: '{config_path}'")
485
503
  raise e
486
504
 
505
+ return self._load_servers_from_dict(config=data, strict=strict)
506
+
507
+ def _load_servers_from_dict(
508
+ self, config: Dict[str, Any], strict: Optional[bool] = False
509
+ ) -> List[MCPClient]:
510
+ r"""Loads MCP server configurations from a dictionary.
511
+
512
+ Args:
513
+ config (Dict[str, Any]): Dictionary containing server
514
+ configurations.
515
+ strict (bool): Whether to enforce strict mode for the
516
+ function call. (default: :obj:`False`)
517
+
518
+ Returns:
519
+ List[MCPClient]: List of configured MCPClient instances.
520
+ """
487
521
  all_servers = []
488
522
 
489
- mcp_servers = data.get("mcpServers", {})
523
+ mcp_servers = config.get("mcpServers", {})
490
524
  if not isinstance(mcp_servers, dict):
491
525
  logger.warning("'mcpServers' is not a dictionary, skipping...")
492
526
  mcp_servers = {}
@@ -505,11 +539,16 @@ class MCPToolkit(BaseToolkit):
505
539
  )
506
540
  continue
507
541
 
542
+ # Include headers if provided in the configuration
543
+ headers = cfg.get("headers", {})
544
+
545
+ cmd_or_url = cast(str, cfg.get("command") or cfg.get("url"))
508
546
  server = MCPClient(
509
- command_or_url=cast(str, cfg.get("command") or cfg.get("url")),
547
+ command_or_url=cmd_or_url,
510
548
  args=cfg.get("args", []),
511
549
  env={**os.environ, **cfg.get("env", {})},
512
550
  timeout=cfg.get("timeout", None),
551
+ headers=headers,
513
552
  strict=strict,
514
553
  )
515
554
  all_servers.append(server)
@@ -0,0 +1,69 @@
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 List, Optional
16
+
17
+ from camel.toolkits import BaseToolkit, FunctionTool
18
+
19
+ from .mcp_toolkit import MCPToolkit
20
+
21
+
22
+ class PlaywrightMCPToolkit(BaseToolkit):
23
+ r"""PlaywrightMCPToolkit provides an interface for interacting with web
24
+ browsers using the Playwright automation library through the Model Context
25
+ Protocol (MCP).
26
+
27
+ Attributes:
28
+ timeout (Optional[float]): Connection timeout in seconds.
29
+ (default: :obj:`None`)
30
+
31
+ Note:
32
+ Currently only supports asynchronous operation mode.
33
+ """
34
+
35
+ def __init__(self, timeout: Optional[float] = None) -> None:
36
+ r"""Initializes the PlaywrightMCPToolkit with the specified timeout.
37
+
38
+ Args:
39
+ timeout (Optional[float]): Connection timeout in seconds.
40
+ (default: :obj:`None`)
41
+ """
42
+ super().__init__(timeout=timeout)
43
+
44
+ self._mcp_toolkit = MCPToolkit(
45
+ config_dict={
46
+ "mcpServers": {
47
+ "playwright": {
48
+ "command": "npx",
49
+ "args": ["@playwright/mcp@latest"],
50
+ }
51
+ }
52
+ }
53
+ )
54
+
55
+ async def connect(self):
56
+ r"""Explicitly connect to the Playwright MCP server."""
57
+ await self._mcp_toolkit.connect()
58
+
59
+ async def disconnect(self):
60
+ r"""Explicitly disconnect from the Playwright MCP server."""
61
+ await self._mcp_toolkit.disconnect()
62
+
63
+ def get_tools(self) -> List[FunctionTool]:
64
+ r"""Returns a list of tools provided by the PlaywrightMCPToolkit.
65
+
66
+ Returns:
67
+ List[FunctionTool]: List of available tools.
68
+ """
69
+ return self._mcp_toolkit.get_tools()
@@ -0,0 +1,132 @@
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
+ from typing import Any, Dict, List, Optional
15
+
16
+ import requests
17
+
18
+ from camel.toolkits.base import BaseToolkit
19
+ from camel.toolkits.function_tool import FunctionTool
20
+ from camel.utils import MCPServer
21
+
22
+
23
+ @MCPServer()
24
+ class PulseMCPSearchToolkit(BaseToolkit):
25
+ r"""A toolkit for searching MCP servers using the PulseMCP API."""
26
+
27
+ def __init__(self, timeout: Optional[float] = None):
28
+ super().__init__(timeout=timeout)
29
+ self.api_base_url = "https://api.pulsemcp.com/v0beta"
30
+
31
+ def search_mcp_servers(
32
+ self,
33
+ query: Optional[str] = None,
34
+ top_k: Optional[int] = 5,
35
+ package_registry: Optional[str] = None,
36
+ count_per_page: int = 5000,
37
+ offset: int = 0,
38
+ ) -> Dict[str, Any]:
39
+ r"""Search for MCP servers using the PulseMCP API.
40
+
41
+ Args:
42
+ query (Optional[str]): The query to search for.
43
+ (default: :obj:`None`)
44
+ top_k (Optional[int]): After sorting, return only the top_k
45
+ servers. (default: :obj:`5`)
46
+ package_registry (Optional[str]): The package registry to search
47
+ for. (default: :obj:`None`)
48
+ count_per_page (int): The number of servers to return per page.
49
+ (default: :obj:`5000`)
50
+ offset (int): The offset to start the search from.
51
+ (default: :obj:`0`)
52
+
53
+ Returns:
54
+ Dict[str, Any]: A dictionary containing the search results or
55
+ an error message.
56
+ """
57
+ params: Dict[str, Any] = {
58
+ "count_per_page": min(count_per_page, 5000),
59
+ "offset": offset,
60
+ }
61
+ if query:
62
+ params["query"] = query
63
+
64
+ response = requests.get(f"{self.api_base_url}/servers", params=params)
65
+
66
+ if response.status_code != 200:
67
+ error_msg = f"Error searching MCP servers: {response.status_code}"
68
+ if response.text:
69
+ try:
70
+ error_info = response.json()
71
+ if "error" in error_info:
72
+ msg = error_info['error'].get('message', '')
73
+ error_msg = f"{error_msg} - {msg}"
74
+ except Exception:
75
+ error_msg = f"{error_msg} - {response.text}"
76
+ return {"error": error_msg}
77
+
78
+ data = response.json()
79
+ servers = data.get("servers", [])
80
+
81
+ if package_registry:
82
+ package_registry_lower = package_registry.lower()
83
+ servers = [
84
+ server
85
+ for server in servers
86
+ if isinstance(server.get("package_registry"), str)
87
+ and server.get("package_registry").lower()
88
+ == package_registry_lower
89
+ ]
90
+
91
+ if query:
92
+ query_lower = query.lower()
93
+ # Helper function to calculate score
94
+ # 1. With name +5
95
+ # 2. With description +3
96
+ # 3. With stars +1
97
+
98
+ def score(server: Dict[str, Any]) -> float:
99
+ name = server.get("name", "").lower()
100
+ desc = server.get("short_description", "").lower()
101
+ stars = server.get("github_stars", 0) or 0
102
+ score: float = 0.0
103
+ if query_lower in name:
104
+ score += 5
105
+ if query_lower in desc:
106
+ score += 3
107
+ score += stars / 1000
108
+ return score
109
+
110
+ servers = sorted(servers, key=score, reverse=True)
111
+ else:
112
+ servers = sorted(
113
+ servers,
114
+ key=lambda x: x.get("github_stars", 0) or 0,
115
+ reverse=True,
116
+ )
117
+
118
+ if top_k is not None:
119
+ servers = servers[:top_k]
120
+
121
+ return {"servers": servers}
122
+
123
+ def get_tools(self) -> List[FunctionTool]:
124
+ r"""Returns a list of FunctionTool objects representing the
125
+ functions in the toolkit.
126
+
127
+ Returns:
128
+ List[FunctionTool]: A list of FunctionTool objects.
129
+ """
130
+ return [
131
+ FunctionTool(self.search_mcp_servers),
132
+ ]
camel/types/enums.py CHANGED
@@ -196,6 +196,7 @@ class ModelType(UnifiedModelType, Enum):
196
196
  MISTRAL_MIXTRAL_8x22B = "open-mixtral-8x22b"
197
197
  MISTRAL_NEMO = "open-mistral-nemo"
198
198
  MISTRAL_PIXTRAL_12B = "pixtral-12b-2409"
199
+ MISTRAL_MEDIUM_3 = "mistral-medium-latest"
199
200
 
200
201
  # Reka models
201
202
  REKA_CORE = "reka-core"
@@ -475,6 +476,16 @@ class ModelType(UnifiedModelType, Enum):
475
476
  ModelType.GPT_4_TURBO,
476
477
  ModelType.GPT_4O,
477
478
  ModelType.GPT_4O_MINI,
479
+ ModelType.O1,
480
+ ModelType.O1_PREVIEW,
481
+ ModelType.O1_MINI,
482
+ ModelType.O3_MINI,
483
+ ModelType.GPT_4_5_PREVIEW,
484
+ ModelType.GPT_4_1,
485
+ ModelType.GPT_4_1_MINI,
486
+ ModelType.GPT_4_1_NANO,
487
+ ModelType.O4_MINI,
488
+ ModelType.O3,
478
489
  }
479
490
 
480
491
  @property
@@ -586,6 +597,7 @@ class ModelType(UnifiedModelType, Enum):
586
597
  ModelType.MISTRAL_PIXTRAL_12B,
587
598
  ModelType.MISTRAL_8B,
588
599
  ModelType.MISTRAL_3B,
600
+ ModelType.MISTRAL_MEDIUM_3,
589
601
  }
590
602
 
591
603
  @property
@@ -1094,6 +1106,7 @@ class ModelType(UnifiedModelType, Enum):
1094
1106
  ModelType.NETMIND_DEEPSEEK_R1,
1095
1107
  ModelType.NETMIND_DEEPSEEK_V3,
1096
1108
  ModelType.NOVITA_DEEPSEEK_V3_0324,
1109
+ ModelType.MISTRAL_MEDIUM_3,
1097
1110
  }:
1098
1111
  return 128_000
1099
1112
  elif self in {
@@ -1389,6 +1402,7 @@ class ModelPlatformType(Enum):
1389
1402
  MISTRAL = "mistral"
1390
1403
  REKA = "reka"
1391
1404
  TOGETHER = "together"
1405
+ STUB = "stub"
1392
1406
  OPENAI_COMPATIBLE_MODEL = "openai-compatible-model"
1393
1407
  SAMBA = "samba-nova"
1394
1408
  COHERE = "cohere"
@@ -1577,6 +1591,11 @@ class ModelPlatformType(Enum):
1577
1591
  r"""Returns whether this platform is Novita."""
1578
1592
  return self is ModelPlatformType.NOVITA
1579
1593
 
1594
+ @property
1595
+ def is_watsonx(self) -> bool:
1596
+ r"""Returns whether this platform is WatsonX."""
1597
+ return self is ModelPlatformType.WATSONX
1598
+
1580
1599
 
1581
1600
  class AudioModelType(Enum):
1582
1601
  TTS_1 = "tts-1"
camel/utils/__init__.py CHANGED
@@ -44,7 +44,7 @@ from .constants import Constants
44
44
  from .deduplication import DeduplicationResult, deduplicate_internally
45
45
  from .filename import sanitize_filename
46
46
  from .mcp import MCPServer
47
- from .response_format import get_pydantic_model
47
+ from .response_format import get_pydantic_model, model_from_json_schema
48
48
  from .token_counting import (
49
49
  AnthropicTokenCounter,
50
50
  BaseTokenCounter,
@@ -92,6 +92,7 @@ __all__ = [
92
92
  "BatchProcessor",
93
93
  "with_timeout",
94
94
  "MCPServer",
95
+ "model_from_json_schema",
95
96
  "sanitize_filename",
96
97
  "browser_toolkit_save_auth_cookie",
97
98
  ]
@@ -16,9 +16,9 @@ from __future__ import annotations
16
16
 
17
17
  import inspect
18
18
  import json
19
- from typing import Callable, Type, Union
19
+ from typing import Any, Callable, Dict, List, Optional, Type, Union
20
20
 
21
- from pydantic import BaseModel, create_model
21
+ from pydantic import BaseModel, Field, create_model
22
22
 
23
23
 
24
24
  def get_pydantic_model(
@@ -61,3 +61,85 @@ def get_pydantic_model(
61
61
  if issubclass(input_data, BaseModel):
62
62
  return input_data
63
63
  raise ValueError("Invalid input data provided.")
64
+
65
+
66
+ TYPE_MAPPING = {
67
+ "integer": int,
68
+ "number": float,
69
+ "string": str,
70
+ "boolean": bool,
71
+ "array": list,
72
+ "object": dict,
73
+ }
74
+
75
+
76
+ def model_from_json_schema(
77
+ name: str,
78
+ schema: Dict[str, Any],
79
+ ) -> Type[BaseModel]:
80
+ r"""Create a Pydantic model from a JSON schema.
81
+
82
+ Args:
83
+ name (str): The name of the model.
84
+ schema (Dict[str, Any]): The JSON schema to create the model from.
85
+
86
+ Returns:
87
+ Type[BaseModel]: The Pydantic model.
88
+ """
89
+ properties = schema.get("properties", {})
90
+ required_fields = set(schema.get("required", []))
91
+ fields: Dict[str, Any] = {}
92
+
93
+ for field_name, field_schema in properties.items():
94
+ json_type = field_schema.get("type", "string")
95
+ # Handle nested objects recursively.
96
+ if json_type == "object" and "properties" in field_schema:
97
+ py_type = model_from_json_schema(
98
+ f"{name}_{field_name}", field_schema
99
+ )
100
+ elif json_type == "array":
101
+ # Process array items if available.
102
+ items_schema = field_schema.get("items", {"type": "string"})
103
+ items_type: Type[Any] = TYPE_MAPPING.get(
104
+ items_schema.get("type", "string"), str
105
+ )
106
+ if (
107
+ items_schema.get("type") == "object"
108
+ and "properties" in items_schema
109
+ ):
110
+ items_type = model_from_json_schema(
111
+ f"{name}_{field_name}_item", items_schema
112
+ )
113
+ py_type = List[items_type] # type: ignore[assignment, valid-type]
114
+ else:
115
+ py_type = TYPE_MAPPING.get(json_type, str)
116
+
117
+ # Handle nullable fields.
118
+ if field_schema.get("nullable", False):
119
+ py_type = Optional[py_type] # type: ignore[assignment]
120
+
121
+ # Construct constraints if available.
122
+ constraints = {}
123
+ if "minLength" in field_schema:
124
+ constraints["min_length"] = field_schema["minLength"]
125
+ if "maxLength" in field_schema:
126
+ constraints["max_length"] = field_schema["maxLength"]
127
+ if "minimum" in field_schema:
128
+ constraints["ge"] = field_schema["minimum"]
129
+ if "maximum" in field_schema:
130
+ constraints["le"] = field_schema["maximum"]
131
+ if "enum" in field_schema:
132
+ constraints["enum"] = field_schema["enum"]
133
+ if "description" in field_schema:
134
+ constraints["description"] = field_schema["description"]
135
+
136
+ default_value = field_schema.get("default", None)
137
+ if field_name in required_fields:
138
+ fields[field_name] = (py_type, Field(..., **constraints))
139
+ else:
140
+ fields[field_name] = (
141
+ Optional[py_type],
142
+ Field(default_value, **constraints),
143
+ )
144
+
145
+ return create_model(name, **fields) # type: ignore[call-overload]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: camel-ai
3
- Version: 0.2.54
3
+ Version: 0.2.56
4
4
  Summary: Communicative Agents for AI Society Study
5
5
  Project-URL: Homepage, https://www.camel-ai.org/
6
6
  Project-URL: Repository, https://github.com/camel-ai/camel
@@ -1,4 +1,4 @@
1
- camel/__init__.py,sha256=6mC_LzkYTRDhA5zSGpvdPlBiTCWzoJKYiy2eQWFw4HU,912
1
+ camel/__init__.py,sha256=YtkzoktMmiyIItoI8OWaV4sxG0yQ8KBjS65mn83J5Jo,912
2
2
  camel/generators.py,sha256=JRqj9_m1PF4qT6UtybzTQ-KBT9MJQt18OAAYvQ_fr2o,13844
3
3
  camel/human.py,sha256=9X09UmxI2JqQnhrFfnZ3B9EzFmVfdSWQcjLWTIXKXe0,4962
4
4
  camel/logger.py,sha256=rZVeOVYuQ9RYJ5Tqyv0usqy0g4zaVEq4qSfZ9nd2640,5755
@@ -7,7 +7,7 @@ camel/agents/__init__.py,sha256=64weKqdvmpZcGWyVkO-OKASAmVUdrQjv60JApgPk_SA,1644
7
7
  camel/agents/_types.py,sha256=ryPRmEXnpNtbFT23GoAcwK-zxWWsIOqYu64mxMx_PhI,1430
8
8
  camel/agents/_utils.py,sha256=AR7Qqgbkmn4X2edYUQf1rdksGUyV5hm3iK1z-Dn0Mcg,6266
9
9
  camel/agents/base.py,sha256=c4bJYL3G3Z41SaFdMPMn8ZjLdFiFaVOFO6EQIfuCVR8,1124
10
- camel/agents/chat_agent.py,sha256=DTP9-tFryDu4ezbrVUS-H671ccBXZa4Tz7KxARawtyw,58962
10
+ camel/agents/chat_agent.py,sha256=s19qJ0eK_bVX6P-yIoazO4cMUeAumwVY1XFNVKaDfVo,59281
11
11
  camel/agents/critic_agent.py,sha256=qFVlHlQo0CVgmPWfWYLT8_oP_KyzCLFsQw_nN_vu5Bs,7487
12
12
  camel/agents/deductive_reasoner_agent.py,sha256=6BZGaq1hR6hKJuQtOfoYQnk_AkZpw_Mr7mUy2MspQgs,13540
13
13
  camel/agents/embodied_agent.py,sha256=XBxBu5ZMmSJ4B2U3Z7SMwvLlgp6yNpaBe8HNQmY9CZA,7536
@@ -176,8 +176,8 @@ camel/models/groq_model.py,sha256=596VqRJ_yxv9Jz3sG7UVXVkIjZI1nX7zQAD709m4uig,37
176
176
  camel/models/internlm_model.py,sha256=l7WjJ7JISCCqkezhEXzmjj_Mvhqhxxhsg4NuenP7w9w,4374
177
177
  camel/models/litellm_model.py,sha256=rlSt3EnBAAYyoIxq0_XTuRmRnc4RWvD2Z14yIrI_7uw,5942
178
178
  camel/models/lmstudio_model.py,sha256=_Lnv0e2ichks_MrNJGNIawEtGtP7T_xX8v0bFNNeWes,3641
179
- camel/models/mistral_model.py,sha256=lsvfxhwp9aDGRpGKLt6Mwtnlw27jFp8AbpGw0UTHJds,12186
180
- camel/models/model_factory.py,sha256=dS5K44jAa1atbicYwKfNFeSVGlgR97rNTQ74Gm_GMOE,12700
179
+ camel/models/mistral_model.py,sha256=3tT59xJO0rwZK0Gs0RXtV6TC9g6uEO9gD7D_-NzhHDc,13399
180
+ camel/models/model_factory.py,sha256=e-dRSZgNGXCgjgdxDl125qtdts7o9M0RQ6mY8qzoBO0,11470
181
181
  camel/models/model_manager.py,sha256=gfpL-WUxuTXgNeCkIVg8Y0zRvxMqRLX8JGt0XEAPQ8Y,9214
182
182
  camel/models/modelscope_model.py,sha256=aI7i50DSIE6MO2U_WvULan6Sk4b5d7iZoEHQaARo4FA,10487
183
183
  camel/models/moonshot_model.py,sha256=yeD2jrfQpFaWJHVe1s1KrI6aHQVzKRcBDt9C2Qo4nU8,4305
@@ -295,7 +295,7 @@ camel/terminators/__init__.py,sha256=t8uqrkUnXEOYMXQDgaBkMFJ0EXFKI0kmx4cUimli3Ls
295
295
  camel/terminators/base.py,sha256=xmJzERX7GdSXcxZjAHHODa0rOxRChMSRboDCNHWSscs,1511
296
296
  camel/terminators/response_terminator.py,sha256=n3G5KP6Oj7-7WlRN0yFcrtLpqAJKaKS0bmhrWlFfCgQ,4982
297
297
  camel/terminators/token_limit_terminator.py,sha256=YWv6ZR8R9yI2Qnf_3xES5bEE_O5bb2CxQ0EUXfMh34c,2118
298
- camel/toolkits/__init__.py,sha256=Vv5yd6_14j6lMhb8XF9R5GBieeLyheM683uVemJaIH4,4483
298
+ camel/toolkits/__init__.py,sha256=JbvxFf-wUFClNSriXB56LkhaRipw4n4PEc2M9f5-47o,4657
299
299
  camel/toolkits/aci_toolkit.py,sha256=jhXMQggG22hd3dXdT3iJm7qWTH3KJC-TUVk1txoNWrM,16079
300
300
  camel/toolkits/arxiv_toolkit.py,sha256=Bs2-K1yfmqhEhHoQ0j00KoI8LpOd8M3ApXcvI_-ApVw,6303
301
301
  camel/toolkits/ask_news_toolkit.py,sha256=WfWaqwEo1Apbil3-Rb5y65Ws43NU4rAFWZu5VHe4los,23448
@@ -316,10 +316,10 @@ camel/toolkits/google_scholar_toolkit.py,sha256=WQ9a0HQr0vro1Uo1m--alCUXvMmaHVKe
316
316
  camel/toolkits/human_toolkit.py,sha256=ZbhXPA0G3mQkcNW_jPwywReAft2pIJG0WkVXOG48s1w,2328
317
317
  camel/toolkits/image_analysis_toolkit.py,sha256=gnbX2by7_pxPpjUEY8bOj1tV-hGC3mwqEvQRMXyZ9TM,7535
318
318
  camel/toolkits/jina_reranker_toolkit.py,sha256=U-V9qZ7SKP3SPTh_0CsE7ZKNqS9jNkmKYsIN1Mk-vFY,8118
319
- camel/toolkits/klavis_toolkit.py,sha256=L-OiGwgaIue8QiIijNlvGbgtwjGUszLqs9bVRW5md7c,8244
319
+ camel/toolkits/klavis_toolkit.py,sha256=ZKerhgz5e-AV-iv0ftf07HgWikknIHjB3EOQswfuR80,9864
320
320
  camel/toolkits/linkedin_toolkit.py,sha256=wn4eXwYYlVA7doTna7k7WYhUqTBF83W79S-UJs_IQr0,8065
321
321
  camel/toolkits/math_toolkit.py,sha256=KdI8AJ9Dbq5cfWboAYJUYgSkmADMCO5eZ6yqYkWuiIQ,3686
322
- camel/toolkits/mcp_toolkit.py,sha256=yBpF6L3mzIs4Ecr3VAyex61CerENOR7ekU0oqAG9WNQ,20449
322
+ camel/toolkits/mcp_toolkit.py,sha256=a2Jdf0TyxqB-P1hB_dh33nH88cEunFMnh7qjgs1H1aQ,21996
323
323
  camel/toolkits/memory_toolkit.py,sha256=TeKYd5UMwgjVpuS2orb-ocFL13eUNKujvrFOruDCpm8,4436
324
324
  camel/toolkits/meshy_toolkit.py,sha256=NbgdOBD3FYLtZf-AfonIv6-Q8-8DW129jsaP1PqI2rs,7126
325
325
  camel/toolkits/mineru_toolkit.py,sha256=vRX9LholLNkpbJ6axfEN4pTG85aWb0PDmlVy3rAAXhg,6868
@@ -329,7 +329,9 @@ camel/toolkits/open_api_toolkit.py,sha256=Venfq8JwTMQfzRzzB7AYmYUMEX35hW0BjIv_oz
329
329
  camel/toolkits/openai_agent_toolkit.py,sha256=hT2ancdQigngAiY1LNnGJzZeiBDHUxrRGv6BdZTJizc,4696
330
330
  camel/toolkits/openbb_toolkit.py,sha256=8yBZL9E2iSgskosBQhD3pTP56oV6gerWpFjIJc_2UMo,28935
331
331
  camel/toolkits/page_script.js,sha256=gypbuQ_gn_oa3rQDoCN_q-kJ0jND1eSvY-30PufPZmQ,12613
332
+ camel/toolkits/playwright_mcp_toolkit.py,sha256=_TcCRA3ECaWO0pqUjoZ2whfWXgvank4A4isqNjgZqqc,2403
332
333
  camel/toolkits/pubmed_toolkit.py,sha256=VGl8KeyWi7pjb2kEhFBLmpBlP9ezv8JyWRHtEVTQ6nQ,12227
334
+ camel/toolkits/pulse_mcp_search_toolkit.py,sha256=uLUpm19uC_4xLJow0gGVS9f-5T5EW2iRAXdJ4nqJG-A,4783
333
335
  camel/toolkits/pyautogui_toolkit.py,sha256=Q810fm8cFvElRory7B74aqS2YV6BOpdRE6jkewoM8xc,16093
334
336
  camel/toolkits/reddit_toolkit.py,sha256=x0XAT1zQJVNHUr1R1HwWCgIlkamU-kPmbfb_H1WIv-w,8036
335
337
  camel/toolkits/retrieval_toolkit.py,sha256=BKjEyOqW3cGEPTS5yHPYb-Qg795iNNPIs1wjowfuq3U,3825
@@ -373,19 +375,19 @@ camel/toolkits/open_api_specs/web_scraper/openapi.yaml,sha256=u_WalQ01e8W1D27VnZ
373
375
  camel/toolkits/open_api_specs/web_scraper/paths/__init__.py,sha256=OKCZrQCDwaWtXIN_2rA9FSqEvgpQRieRoHh7Ek6N16A,702
374
376
  camel/toolkits/open_api_specs/web_scraper/paths/scraper.py,sha256=aWy1_ppV4NVVEZfnbN3tu9XA9yAPAC9bRStJ5JuXMRU,1117
375
377
  camel/types/__init__.py,sha256=Xdkjh7TQDAQUQ7pFnt7i_rLjjOhsJJnhzusARcFc94Q,2311
376
- camel/types/enums.py,sha256=4X3QusgMsmJhvAAoWj5r3PcUg7HyiQwRii4pVE7QLr8,57696
378
+ camel/types/enums.py,sha256=ULkVZwiCBO6tjemF8V_A7BlYsSWJH1owp2UVDE39N4A,58317
377
379
  camel/types/openai_types.py,sha256=8ZFzLe-zGmKNPfuVZFzxlxAX98lGf18gtrPhOgMmzus,2104
378
380
  camel/types/unified_model_type.py,sha256=TpiUmJ3IuX8LNLtTUeUcVM7U82r4ClSq3ZQlNX3ODKs,5351
379
381
  camel/types/agents/__init__.py,sha256=cbvVkogPoZgcwZrgxLH6EtpGXk0kavF79nOic0Dc1vg,786
380
382
  camel/types/agents/tool_calling_record.py,sha256=qa-vLyKvYzWprRkFFl1928xaw9CnfacIebHaqM-oY3s,1814
381
- camel/utils/__init__.py,sha256=R2VaBRjYDslfF0qlPgE7-EIdcTNV-mQMWXbEAZ0CsuY,2815
383
+ camel/utils/__init__.py,sha256=v4ugilUllTQGUSjnCfDvpoFGhWo3JMA7JRZlBceftic,2869
382
384
  camel/utils/async_func.py,sha256=KqoktGSWjZBuAMQ2CV0X6FRgHGlzCKLfeaWvp-f1Qz8,1568
383
385
  camel/utils/commons.py,sha256=3XofL3nZ-tZ3DQLWmP41CAp8lT-ictBBsttJdRJjL4Q,36323
384
386
  camel/utils/constants.py,sha256=cqnxmpUeOwrtsR-tRO4bbOc6ZP19TLj7avjm3FONMJs,1410
385
387
  camel/utils/deduplication.py,sha256=UHikAtOW1TTDunf2t_wa2kFbmkrXWf7HfOKwLvwCxzo,8958
386
388
  camel/utils/filename.py,sha256=HYNc1wbSCgNR1CN21cwHxdAhpnsf5ySJ6jUDfeqOK20,2532
387
389
  camel/utils/mcp.py,sha256=iuthL8VuUXIRU34Nvx8guq7frfglpZoxewUKuAg3e1s,5077
388
- camel/utils/response_format.py,sha256=9KrbwtOM9cA3LSjTgLiK7oKy-53_uMh1cvpyNwwJpng,2419
390
+ camel/utils/response_format.py,sha256=xZcx6xBxeg3A0e7R0JCMJdNm2oQ1-diqVLs0JsiCkZU,5319
389
391
  camel/utils/token_counting.py,sha256=apkERzNoVc4sgvJvWVosvepX3KH8pVypVjrL4AA7RB4,17521
390
392
  camel/utils/chunker/__init__.py,sha256=6iN6HL6sblIjDuJTILk-9qKcHBZ97t8b6tZCWPZ0OYI,899
391
393
  camel/utils/chunker/base.py,sha256=9CuqymFCRncyAdEST-IcRonB732YAPhusznqH-RES3E,960
@@ -397,7 +399,7 @@ camel/verifiers/math_verifier.py,sha256=tA1D4S0sm8nsWISevxSN0hvSVtIUpqmJhzqfbuMo
397
399
  camel/verifiers/models.py,sha256=GdxYPr7UxNrR1577yW4kyroRcLGfd-H1GXgv8potDWU,2471
398
400
  camel/verifiers/physics_verifier.py,sha256=c1grrRddcrVN7szkxhv2QirwY9viIRSITWeWFF5HmLs,30187
399
401
  camel/verifiers/python_verifier.py,sha256=ogTz77wODfEcDN4tMVtiSkRQyoiZbHPY2fKybn59lHw,20558
400
- camel_ai-0.2.54.dist-info/METADATA,sha256=d8FvLrmsbJjNmkNMBo5dUHjdCoWCxMn7Xt98QOgE-pY,44254
401
- camel_ai-0.2.54.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
402
- camel_ai-0.2.54.dist-info/licenses/LICENSE,sha256=id0nB2my5kG0xXeimIu5zZrbHLS6EQvxvkKkzIHaT2k,11343
403
- camel_ai-0.2.54.dist-info/RECORD,,
402
+ camel_ai-0.2.56.dist-info/METADATA,sha256=EJjtjqfftYHbjaVC_Nv8Kavlpf32Kj685q-2sE98GH4,44254
403
+ camel_ai-0.2.56.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
404
+ camel_ai-0.2.56.dist-info/licenses/LICENSE,sha256=id0nB2my5kG0xXeimIu5zZrbHLS6EQvxvkKkzIHaT2k,11343
405
+ camel_ai-0.2.56.dist-info/RECORD,,