camel-ai 0.2.69a3__py3-none-any.whl → 0.2.69a6__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.69a3'
17
+ __version__ = '0.2.69a6'
18
18
 
19
19
  __all__ = [
20
20
  '__version__',
@@ -1904,36 +1904,12 @@ class ChatAgent(BaseAgent):
1904
1904
  ChatAgent: A new instance of :obj:`ChatAgent` with the same
1905
1905
  configuration.
1906
1906
  """
1907
- import copy
1908
-
1909
1907
  # Create a new instance with the same configuration
1910
1908
  # If with_memory is True, set system_message to None
1911
1909
  # If with_memory is False, use the original system message
1912
1910
  # To avoid duplicated system memory.
1913
1911
  system_message = None if with_memory else self._original_system_message
1914
1912
 
1915
- # Create deep copies of tools to avoid shared state
1916
- cloned_tools: List[Union[FunctionTool, Callable]] = []
1917
- for tool in self._internal_tools.values():
1918
- # Create a new FunctionTool instance to avoid sharing state
1919
- cloned_tool = copy.deepcopy(tool)
1920
- cloned_tools.append(cloned_tool)
1921
-
1922
- # Create deep copies of external tool schemas
1923
- cloned_external_tools: List[
1924
- Union[FunctionTool, Callable, Dict[str, Any]]
1925
- ] = []
1926
- for schema in self._external_tool_schemas.values():
1927
- cloned_schema = copy.deepcopy(schema)
1928
- cloned_external_tools.append(cloned_schema)
1929
-
1930
- # Create deep copies of response terminators to avoid shared state
1931
- cloned_terminators: List[ResponseTerminator] = []
1932
- if self.response_terminators:
1933
- for terminator in self.response_terminators:
1934
- cloned_terminator = copy.deepcopy(terminator)
1935
- cloned_terminators.append(cloned_terminator)
1936
-
1937
1913
  new_agent = ChatAgent(
1938
1914
  system_message=system_message,
1939
1915
  model=self.model_backend.models, # Pass the existing model_backend
@@ -1943,9 +1919,11 @@ class ChatAgent(BaseAgent):
1943
1919
  self.memory.get_context_creator(), "token_limit", None
1944
1920
  ),
1945
1921
  output_language=self._output_language,
1946
- tools=cloned_tools,
1947
- external_tools=cloned_external_tools,
1948
- response_terminators=cloned_terminators,
1922
+ tools=[tool.func for tool in self._internal_tools.values()],
1923
+ external_tools=[
1924
+ schema for schema in self._external_tool_schemas.values()
1925
+ ],
1926
+ response_terminators=self.response_terminators,
1949
1927
  scheduling_strategy=self.model_backend.scheduling_strategy.__name__,
1950
1928
  max_iteration=self.max_iteration,
1951
1929
  stop_event=self.stop_event,
@@ -79,6 +79,9 @@ class InternalPythonInterpreter(BaseInterpreter):
79
79
  (default: :obj:`False`)
80
80
  raise_error (bool, optional): Raise error if the interpreter fails.
81
81
  (default: :obj:`False`)
82
+ allow_builtins (bool, optional): If `True`, safe built-in functions
83
+ like print, len, str, etc. are added to the action space.
84
+ (default: :obj:`True`)
82
85
  """
83
86
 
84
87
  _CODE_TYPES: ClassVar[List[str]] = ["python", "py", "python3", "python2"]
@@ -89,16 +92,62 @@ class InternalPythonInterpreter(BaseInterpreter):
89
92
  import_white_list: Optional[List[str]] = None,
90
93
  unsafe_mode: bool = False,
91
94
  raise_error: bool = False,
95
+ allow_builtins: bool = True,
92
96
  ) -> None:
93
97
  self.action_space = action_space or dict()
94
- # Add print to action space
95
- self.action_space['print'] = print
96
98
  self.state = self.action_space.copy()
97
99
  self.fuzz_state: Dict[str, Any] = dict()
98
100
  self.import_white_list = import_white_list or list()
99
101
  self.raise_error = raise_error
100
102
  self.unsafe_mode = unsafe_mode
101
103
 
104
+ # Add safe built-in functions if allowed
105
+ if allow_builtins:
106
+ self._add_safe_builtins()
107
+
108
+ def _add_safe_builtins(self):
109
+ r"""Add safe built-in functions to the action space."""
110
+ safe_builtins = {
111
+ 'print': print,
112
+ 'len': len,
113
+ 'str': str,
114
+ 'int': int,
115
+ 'float': float,
116
+ 'bool': bool,
117
+ 'list': list,
118
+ 'dict': dict,
119
+ 'tuple': tuple,
120
+ 'set': set,
121
+ 'abs': abs,
122
+ 'min': min,
123
+ 'max': max,
124
+ 'sum': sum,
125
+ 'sorted': sorted,
126
+ 'reversed': reversed,
127
+ 'enumerate': enumerate,
128
+ 'zip': zip,
129
+ 'range': range,
130
+ 'round': round,
131
+ 'type': type,
132
+ 'isinstance': isinstance,
133
+ 'hasattr': hasattr,
134
+ 'getattr': getattr,
135
+ 'setattr': setattr,
136
+ 'dir': dir,
137
+ 'help': help,
138
+ 'map': map,
139
+ 'filter': filter,
140
+ 'any': any,
141
+ 'all': all,
142
+ 'ord': ord,
143
+ 'chr': chr,
144
+ 'bin': bin,
145
+ 'oct': oct,
146
+ 'hex': hex,
147
+ }
148
+ self.action_space.update(safe_builtins)
149
+ self.state.update(safe_builtins)
150
+
102
151
  def run(self, code: str, code_type: str = "python") -> str:
103
152
  r"""Executes the given code with specified code type in the
104
153
  interpreter.
@@ -12,6 +12,7 @@
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  import abc
15
+ import os
15
16
  import re
16
17
  from abc import ABC, abstractmethod
17
18
  from typing import Any, Dict, List, Optional, Type, Union
@@ -94,6 +95,12 @@ class BaseModelBackend(ABC, metaclass=ModelBackendMeta):
94
95
  self._token_counter = token_counter
95
96
  self._timeout = timeout
96
97
  self._max_retries = max_retries
98
+ # Initialize logging configuration
99
+ self._log_enabled = (
100
+ os.environ.get("CAMEL_MODEL_LOG_ENABLED", "False").lower()
101
+ == "true"
102
+ )
103
+ self._log_dir = os.environ.get("CAMEL_LOG_DIR", "camel_logs")
97
104
  self.check_model_config()
98
105
 
99
106
  @property
@@ -232,6 +239,68 @@ class BaseModelBackend(ABC, metaclass=ModelBackendMeta):
232
239
 
233
240
  return formatted_messages
234
241
 
242
+ def _log_request(self, messages: List[OpenAIMessage]) -> Optional[str]:
243
+ r"""Log the request messages to a JSON file if logging is enabled.
244
+
245
+ Args:
246
+ messages (List[OpenAIMessage]): The messages to log.
247
+
248
+ Returns:
249
+ Optional[str]: The path to the log file if logging is enabled,
250
+ None otherwise.
251
+ """
252
+ if not self._log_enabled:
253
+ return None
254
+
255
+ import json
256
+ from datetime import datetime
257
+
258
+ os.makedirs(self._log_dir, exist_ok=True)
259
+
260
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S_%f')
261
+ log_file_path = os.path.join(self._log_dir, f"conv_{timestamp}.json")
262
+
263
+ log_entry = {
264
+ "request_timestamp": datetime.now().isoformat(),
265
+ "model": str(self.model_type),
266
+ "request": {"messages": messages},
267
+ }
268
+
269
+ with open(log_file_path, "w") as f:
270
+ json.dump(log_entry, f, indent=4)
271
+
272
+ return log_file_path
273
+
274
+ def _log_response(self, log_path: str, response: Any) -> None:
275
+ r"""Log the response to the existing log file.
276
+
277
+ Args:
278
+ log_path (str): The path to the log file.
279
+ response (Any): The response to log.
280
+ """
281
+ if not self._log_enabled or not log_path:
282
+ return
283
+
284
+ import json
285
+ from datetime import datetime
286
+
287
+ with open(log_path, "r+") as f:
288
+ log_data = json.load(f)
289
+
290
+ log_data["response_timestamp"] = datetime.now().isoformat()
291
+ if isinstance(response, BaseModel):
292
+ log_data["response"] = response.model_dump()
293
+ else:
294
+ try:
295
+ json.dumps(response)
296
+ log_data["response"] = response
297
+ except TypeError:
298
+ log_data["response"] = str(response)
299
+
300
+ f.seek(0)
301
+ json.dump(log_data, f, indent=4)
302
+ f.truncate()
303
+
235
304
  @abstractmethod
236
305
  def _run(
237
306
  self,
@@ -273,13 +342,23 @@ class BaseModelBackend(ABC, metaclass=ModelBackendMeta):
273
342
  `ChatCompletion` in the non-stream mode, or
274
343
  `Stream[ChatCompletionChunk]` in the stream mode.
275
344
  """
345
+ # Log the request if logging is enabled
346
+ log_path = self._log_request(messages)
347
+
276
348
  # None -> use default tools
277
349
  if tools is None:
278
350
  tools = self.model_config_dict.get("tools", None)
279
351
  # Empty -> use no tools
280
352
  elif not tools:
281
353
  tools = None
282
- return self._run(messages, response_format, tools)
354
+
355
+ result = self._run(messages, response_format, tools)
356
+
357
+ # Log the response if logging is enabled
358
+ if log_path:
359
+ self._log_response(log_path, result)
360
+
361
+ return result
283
362
 
284
363
  async def arun(
285
364
  self,
@@ -304,11 +383,21 @@ class BaseModelBackend(ABC, metaclass=ModelBackendMeta):
304
383
  `ChatCompletion` in the non-stream mode, or
305
384
  `AsyncStream[ChatCompletionChunk]` in the stream mode.
306
385
  """
386
+ # Log the request if logging is enabled
387
+ log_path = self._log_request(messages)
388
+
307
389
  if tools is None:
308
390
  tools = self.model_config_dict.get("tools", None)
309
391
  elif not tools:
310
392
  tools = None
311
- return await self._arun(messages, response_format, tools)
393
+
394
+ result = await self._arun(messages, response_format, tools)
395
+
396
+ # Log the response if logging is enabled
397
+ if log_path:
398
+ self._log_response(log_path, result)
399
+
400
+ return result
312
401
 
313
402
  @abstractmethod
314
403
  def check_model_config(self):
@@ -284,6 +284,13 @@ class SingleAgentWorker(Worker):
284
284
  response = await worker_agent.astep(
285
285
  prompt, response_format=TaskResult
286
286
  )
287
+
288
+ # Get token usage from the response
289
+ usage_info = response.info.get("usage") or response.info.get(
290
+ "token_usage"
291
+ )
292
+ total_tokens = usage_info.get("total_tokens", 0)
293
+
287
294
  except Exception as e:
288
295
  print(
289
296
  f"{Fore.RED}Error processing task {task.id}: "
@@ -294,13 +301,6 @@ class SingleAgentWorker(Worker):
294
301
  # Return agent to pool or let it be garbage collected
295
302
  await self._return_worker_agent(worker_agent)
296
303
 
297
- # Get actual token usage from the agent that processed this task
298
- try:
299
- _, total_token_count = worker_agent.memory.get_context()
300
- except Exception:
301
- # Fallback if memory context unavailable
302
- total_token_count = 0
303
-
304
304
  # Populate additional_info with worker attempt details
305
305
  if task.additional_info is None:
306
306
  task.additional_info = {}
@@ -321,7 +321,7 @@ class SingleAgentWorker(Worker):
321
321
  f"to process task {task.content}",
322
322
  "response_content": response.msg.content,
323
323
  "tool_calls": response.info.get("tool_calls"),
324
- "total_token_count": total_token_count,
324
+ "total_tokens": total_tokens,
325
325
  }
326
326
 
327
327
  # Store the worker attempt in additional_info
@@ -330,9 +330,7 @@ class SingleAgentWorker(Worker):
330
330
  task.additional_info["worker_attempts"].append(worker_attempt_details)
331
331
 
332
332
  # Store the actual token usage for this specific task
333
- task.additional_info["token_usage"] = {
334
- "total_tokens": total_token_count
335
- }
333
+ task.additional_info["token_usage"] = {"total_tokens": total_tokens}
336
334
 
337
335
  print(f"======\n{Fore.GREEN}Reply from {self}:{Fore.RESET}")
338
336
 
@@ -78,6 +78,9 @@ from .playwright_mcp_toolkit import PlaywrightMCPToolkit
78
78
  from .wolfram_alpha_toolkit import WolframAlphaToolkit
79
79
  from .task_planning_toolkit import TaskPlanningToolkit
80
80
  from .non_visual_browser_toolkit import BrowserNonVisualToolkit
81
+ from .edgeone_pages_mcp_toolkit import EdgeOnePagesMCPToolkit
82
+ from .craw4ai_toolkit import Crawl4AIToolkit
83
+ from .markitdown_toolkit import MarkItDownToolkit
81
84
 
82
85
 
83
86
  __all__ = [
@@ -144,4 +147,7 @@ __all__ = [
144
147
  'BohriumToolkit',
145
148
  'TaskPlanningToolkit',
146
149
  'BrowserNonVisualToolkit',
150
+ 'EdgeOnePagesMCPToolkit',
151
+ 'Crawl4AIToolkit',
152
+ 'MarkItDownToolkit',
147
153
  ]
@@ -0,0 +1,73 @@
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.logger import get_logger
18
+ from camel.toolkits.base import BaseToolkit
19
+ from camel.toolkits.function_tool import FunctionTool
20
+ from camel.utils import MCPServer
21
+
22
+ logger = get_logger(__name__)
23
+
24
+
25
+ @MCPServer()
26
+ class Crawl4AIToolkit(BaseToolkit):
27
+ r"""A class representing a toolkit for Crawl4AI."""
28
+
29
+ def __init__(
30
+ self,
31
+ timeout: Optional[float] = None,
32
+ ):
33
+ super().__init__(timeout=timeout)
34
+
35
+ async def scrape(self, url: str) -> str:
36
+ r"""Scrapes a webpage and returns its content.
37
+
38
+ This function is designed to fetch the content of a given URL and
39
+ return it as a single string. It's particularly useful for extracting
40
+ text from web pages for further processing. The function
41
+ will try to get the most meaningful content from the page.
42
+
43
+ Args:
44
+ url (str): The URL of the webpage to scrape.
45
+
46
+ Returns:
47
+ str: The scraped content of the webpage as a string. If the
48
+ scraping fails, it will return an error message.
49
+ """
50
+ from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
51
+
52
+ try:
53
+ async with AsyncWebCrawler() as client:
54
+ config = CrawlerRunConfig(
55
+ only_text=True,
56
+ )
57
+ content = await client.arun(url, crawler_config=config)
58
+ return str(content.markdown) if content.markdown else ""
59
+ except Exception as e:
60
+ logger.error(f"Error scraping {url}: {e}")
61
+ return f"Error scraping {url}: {e}"
62
+
63
+ def get_tools(self) -> List[FunctionTool]:
64
+ r"""Returns a list of FunctionTool objects representing the
65
+ functions in the toolkit.
66
+
67
+ Returns:
68
+ List[FunctionTool]: A list of FunctionTool objects
69
+ representing the functions in the toolkit.
70
+ """
71
+ return [
72
+ FunctionTool(self.scrape),
73
+ ]
@@ -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 EdgeOnePagesMCPToolkit(BaseToolkit):
23
+ r"""EdgeOnePagesMCPToolkit provides an interface for interacting with
24
+ EdgeOne pages using the EdgeOne Pages MCP server.
25
+
26
+ Attributes:
27
+ timeout (Optional[float]): Connection timeout in seconds.
28
+ (default: :obj:`None`)
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ timeout: Optional[float] = None,
34
+ ) -> None:
35
+ r"""Initializes the EdgeOnePagesMCPToolkit.
36
+
37
+ Args:
38
+ timeout (Optional[float]): Connection timeout in seconds.
39
+ (default: :obj:`None`)
40
+ """
41
+ super().__init__(timeout=timeout)
42
+
43
+ self._mcp_toolkit = MCPToolkit(
44
+ config_dict={
45
+ "mcpServers": {
46
+ "edgeone-pages-mcp-server": {
47
+ "command": "npx",
48
+ "args": ["edgeone-pages-mcp"],
49
+ }
50
+ }
51
+ },
52
+ timeout=timeout,
53
+ )
54
+
55
+ async def connect(self):
56
+ r"""Explicitly connect to the EdgeOne Pages MCP server."""
57
+ await self._mcp_toolkit.connect()
58
+
59
+ async def disconnect(self):
60
+ r"""Explicitly disconnect from the EdgeOne Pages 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 EdgeOnePagesMCPToolkit.
65
+
66
+ Returns:
67
+ List[FunctionTool]: List of available tools.
68
+ """
69
+ return self._mcp_toolkit.get_tools()
@@ -0,0 +1,78 @@
1
+ # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+ from typing import Dict, List, Optional
16
+
17
+ from camel.loaders.markitdown import MarkItDownLoader
18
+ from camel.logger import get_logger
19
+ from camel.toolkits.base import BaseToolkit
20
+ from camel.toolkits.function_tool import FunctionTool
21
+ from camel.utils import MCPServer
22
+
23
+ logger = get_logger(__name__)
24
+
25
+
26
+ @MCPServer()
27
+ class MarkItDownToolkit(BaseToolkit):
28
+ r"""A class representing a toolkit for MarkItDown."""
29
+
30
+ def __init__(
31
+ self,
32
+ timeout: Optional[float] = None,
33
+ ):
34
+ super().__init__(timeout=timeout)
35
+
36
+ def load_files(self, file_paths: List[str]) -> Dict[str, str]:
37
+ r"""Scrapes content from a list of files and converts it to Markdown.
38
+
39
+ This function takes a list of local file paths, attempts to convert
40
+ each file into Markdown format, and returns the converted content.
41
+ The conversion is performed in parallel for efficiency.
42
+
43
+ Supported file formats include:
44
+ - PDF (.pdf)
45
+ - Microsoft Office: Word (.doc, .docx), Excel (.xls, .xlsx),
46
+ PowerPoint (.ppt, .pptx)
47
+ - EPUB (.epub)
48
+ - HTML (.html, .htm)
49
+ - Images (.jpg, .jpeg, .png) for OCR
50
+ - Audio (.mp3, .wav) for transcription
51
+ - Text-based formats (.csv, .json, .xml, .txt)
52
+ - ZIP archives (.zip)
53
+
54
+ Args:
55
+ file_paths (List[str]): A list of local file paths to be
56
+ converted.
57
+
58
+ Returns:
59
+ Dict[str, str]: A dictionary where keys are the input file paths
60
+ and values are the corresponding content in Markdown format.
61
+ If conversion of a file fails, the value will contain an
62
+ error message.
63
+ """
64
+ return MarkItDownLoader().convert_files(
65
+ file_paths=file_paths, parallel=True
66
+ )
67
+
68
+ def get_tools(self) -> List[FunctionTool]:
69
+ r"""Returns a list of FunctionTool objects representing the
70
+ functions in the toolkit.
71
+
72
+ Returns:
73
+ List[FunctionTool]: A list of FunctionTool objects
74
+ representing the functions in the toolkit.
75
+ """
76
+ return [
77
+ FunctionTool(self.load_files),
78
+ ]
@@ -115,16 +115,19 @@ class BrowserNonVisualToolkit(BaseToolkit):
115
115
  async def open_browser(
116
116
  self, start_url: Optional[str] = None
117
117
  ) -> Dict[str, str]:
118
- r"""Launch a Playwright browser session.
118
+ r"""Launches a new browser session. This should be the first step.
119
119
 
120
120
  Args:
121
- start_url (Optional[str]): Optional URL to navigate to immediately
122
- after the browser launches. If not provided, the browser will
123
- not navigate to any URL. (default: :obj:`None`)
121
+ start_url (Optional[str]): The URL to navigate to after the browser
122
+ launches. If not provided, the browser will open with a blank
123
+ page. (default: :obj:`None`)
124
124
 
125
125
  Returns:
126
- Dict[str, str]: Keys: ``result`` for action outcome,
127
- ``snapshot`` for full DOM snapshot.
126
+ Dict[str, str]: A dictionary containing the result of the action
127
+ and a snapshot of the page. The keys are "result" and
128
+ "snapshot". The "snapshot" is a YAML-like representation of
129
+ the page's DOM structure, including element references
130
+ (e.g., "e3") that can be used in other tool calls.
128
131
  """
129
132
  await self._session.ensure_browser()
130
133
  if start_url:
@@ -136,10 +139,12 @@ class BrowserNonVisualToolkit(BaseToolkit):
136
139
  return {"result": "Browser session started.", "snapshot": snapshot}
137
140
 
138
141
  async def close_browser(self) -> str:
139
- r"""Terminate the current browser session and free all resources.
142
+ r"""Closes the current browser session, freeing up all associated
143
+ resources. This should be called when the browsing task is complete.
140
144
 
141
145
  Returns:
142
- str: Confirmation message.
146
+ str: A confirmation message indicating the session has been
147
+ closed.
143
148
  """
144
149
  # Close agent if it exists
145
150
  if self._agent is not None:
@@ -154,14 +159,17 @@ class BrowserNonVisualToolkit(BaseToolkit):
154
159
  return "Browser session closed."
155
160
 
156
161
  async def visit_page(self, url: str) -> Dict[str, str]:
157
- r"""Navigate the current page to the specified URL.
162
+ r"""Navigates the current browser page to a new URL.
158
163
 
159
164
  Args:
160
- url (str): The destination URL.
165
+ url (str): The URL to navigate to. Must be a fully qualified URL
166
+ (e.g., "https://www.google.com").
161
167
 
162
168
  Returns:
163
- Dict[str, str]: Keys: ``result`` for action outcome,
164
- ``snapshot`` for full DOM snapshot.
169
+ Dict[str, str]: A dictionary containing the navigation result and a
170
+ snapshot of the new page. The keys are "result" and
171
+ "snapshot". The "snapshot" provides a fresh view of the
172
+ page's DOM structure.
165
173
  """
166
174
  if not url or not isinstance(url, str):
167
175
  raise ValueError("visit_page(): 'url' must be a non-empty string")
@@ -192,14 +200,16 @@ class BrowserNonVisualToolkit(BaseToolkit):
192
200
  )
193
201
 
194
202
  async def click(self, *, ref: str) -> Dict[str, str]:
195
- r"""Click an element identified by ``ref``
203
+ r"""Performs a click action on a specified element on the current page.
196
204
 
197
205
  Args:
198
- ref (str): Element reference ID extracted from snapshot
199
- (e.g.``"e3"``).
206
+ ref (str): The reference ID of the element to click. This ID is
207
+ obtained from the page snapshot (e.g., "e12").
200
208
 
201
209
  Returns:
202
- Dict[str, str]: Result message from ``ActionExecutor``.
210
+ Dict[str, str]: A dictionary containing the result of the action.
211
+ If the click causes a change in the page's structure, a
212
+ "snapshot" key will be included with a diff of the changes.
203
213
  """
204
214
  self._validate_ref(ref, "click")
205
215
 
@@ -207,15 +217,16 @@ class BrowserNonVisualToolkit(BaseToolkit):
207
217
  return await self._exec_with_snapshot(action)
208
218
 
209
219
  async def type(self, *, ref: str, text: str) -> Dict[str, str]:
210
- r"""Type text into an input or textarea element.
220
+ r"""Types text into an input field or textarea on the current page.
211
221
 
212
222
  Args:
213
- ref (str): Element reference ID extracted from snapshot.
214
- (e.g.``"e3"``).
215
- text (str): The text to enter.
223
+ ref (str): The reference ID of the input element. This ID is
224
+ obtained from the page snapshot (e.g., "e25").
225
+ text (str): The text to be typed into the element.
216
226
 
217
227
  Returns:
218
- Dict[str, str]: Execution result message.
228
+ Dict[str, str]: A dictionary containing the result of the action.
229
+ The key is "result".
219
230
  """
220
231
  self._validate_ref(ref, "type")
221
232
 
@@ -223,14 +234,17 @@ class BrowserNonVisualToolkit(BaseToolkit):
223
234
  return await self._exec_with_snapshot(action)
224
235
 
225
236
  async def select(self, *, ref: str, value: str) -> Dict[str, str]:
226
- r"""Select an option in a ``<select>`` element.
237
+ r"""Selects an option from a dropdown (<select>) element on the page.
227
238
 
228
239
  Args:
229
- ref (str): Element reference ID.
230
- value (str): The value / option to select.
240
+ ref (str): The reference ID of the <select> element. This ID is
241
+ obtained from the page snapshot.
242
+ value (str): The value of the option to be selected. This should
243
+ match the 'value' attribute of an <option> tag.
231
244
 
232
245
  Returns:
233
- Dict[str, str]: Execution result message.
246
+ Dict[str, str]: A dictionary containing the result of the action.
247
+ The key is "result".
234
248
  """
235
249
  self._validate_ref(ref, "select")
236
250
 
@@ -238,15 +252,16 @@ class BrowserNonVisualToolkit(BaseToolkit):
238
252
  return await self._exec_with_snapshot(action)
239
253
 
240
254
  async def scroll(self, *, direction: str, amount: int) -> Dict[str, str]:
241
- r"""Scroll the page.
255
+ r"""Scrolls the current page up or down by a specified amount.
242
256
 
243
257
  Args:
244
- direction (str): Scroll direction, should be ``"down"`` or
245
- ``"up"``.
246
- amount (int): Pixel distance to scroll.
258
+ direction (str): The direction to scroll. Must be either "up" or
259
+ "down".
260
+ amount (int): The number of pixels to scroll.
247
261
 
248
262
  Returns:
249
- Dict[str, str]: Execution result message.
263
+ Dict[str, str]: A dictionary containing the result of the action.
264
+ The key is "result".
250
265
  """
251
266
  if direction not in ("up", "down"):
252
267
  logger.error("scroll(): 'direction' must be 'up' or 'down'")
@@ -258,53 +273,72 @@ class BrowserNonVisualToolkit(BaseToolkit):
258
273
  return await self._exec_with_snapshot(action)
259
274
 
260
275
  async def enter(self, *, ref: str) -> Dict[str, str]:
261
- r"""Press the Enter key.
276
+ r"""Simulates pressing the Enter key on a specific element.
277
+ This is often used to submit forms.
262
278
 
263
279
  Args:
264
- ref (str): Element reference ID to focus before pressing.
280
+ ref (str): The reference ID of the element to focus before
281
+ pressing Enter. This ID is obtained from the page snapshot.
265
282
 
266
283
  Returns:
267
- Dict[str, str]: Execution result message.
284
+ Dict[str, str]: A dictionary containing the result of the action.
285
+ If pressing Enter causes a page navigation or DOM change, a
286
+ "snapshot" key will be included with a diff of the changes.
268
287
  """
269
288
  self._validate_ref(ref, "enter")
270
289
 
271
290
  action: Dict[str, Any] = {"type": "enter", "ref": ref}
272
291
  return await self._exec_with_snapshot(action)
273
292
 
274
- async def wait_user(self, *, seconds: float = 1.0) -> Dict[str, str]:
275
- r"""Pause execution for a given amount of *real* time and then
276
- return a *full* page snapshot.
277
-
278
- This is a convenience wrapper around the existing wait action for
279
- scenarios where you encounter a CAPTCHA or need to pause for manual
280
- user input, and want to retrieve the complete DOM snapshot afterward.
293
+ async def wait_user(
294
+ self,
295
+ timeout_sec: Optional[float] = None,
296
+ ) -> Dict[str, str]:
297
+ r"""Pauses the agent's execution and waits for human intervention.
298
+ This is useful for tasks that require manual steps, like solving a
299
+ CAPTCHA. The agent will print a message and wait for the user to
300
+ press the Enter key in the console.
281
301
 
282
302
  Args:
283
- seconds (float): How long to sleep, expressed in seconds. Must
284
- be a positive number. (default: :obj:`1.0`)
303
+ timeout_sec (Optional[float]): The maximum time in seconds to wait
304
+ for the user. If `None`, it will wait indefinitely. Defaults
305
+ to `None`. (default: :obj:`None`)
285
306
 
286
307
  Returns:
287
- Dict[str, str]: Keys ``result`` and ``snapshot``.
308
+ Dict[str, str]: A dictionary containing a result message and a
309
+ full snapshot of the current page after the user has acted.
310
+ The keys are "result" and "snapshot".
288
311
  """
289
- if seconds is None or seconds <= 0:
290
- logger.error("wait_time(): 'seconds' must be a positive number")
291
- return {
292
- "result": "wait_time(): 'seconds' must be a positive number"
293
- }
294
312
 
295
- # Reuse underlying ActionExecutor's ``wait`` implementation (expects
296
- # ms)
297
- timeout_ms = int(seconds * 1000)
298
- action: Dict[str, Any] = {"type": "wait", "timeout": timeout_ms}
313
+ import asyncio
314
+
315
+ prompt = (
316
+ "🕑 Agent is waiting for human input. "
317
+ "Complete the required action in the browser, then press Enter "
318
+ "to continue..."
319
+ )
299
320
 
300
- # Execute the sleep via ActionExecutor (no snapshot diff expected)
301
- result = await self._exec(action)
321
+ logger.info(f"\n{prompt}\n")
322
+
323
+ async def _await_enter():
324
+ await asyncio.to_thread(input, ">>> Press Enter to resume <<<\n")
325
+
326
+ try:
327
+ if timeout_sec is not None:
328
+ await asyncio.wait_for(_await_enter(), timeout=timeout_sec)
329
+ result_msg = "User resumed."
330
+ else:
331
+ await _await_enter()
332
+ result_msg = "User resumed."
333
+ except asyncio.TimeoutError:
334
+ result_msg = f"Timeout {timeout_sec}s reached, auto-resumed."
302
335
 
303
- # Always return a *full* snapshot after the pause
304
336
  snapshot = await self._session.get_snapshot(
305
- force_refresh=True, diff_only=False
337
+ force_refresh=True,
338
+ diff_only=False,
306
339
  )
307
- return {"result": result, "snapshot": snapshot}
340
+
341
+ return {"result": result_msg, "snapshot": snapshot}
308
342
 
309
343
  # Helper to run through ActionExecutor
310
344
  async def _exec(self, action: Dict[str, Any]) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: camel-ai
3
- Version: 0.2.69a3
3
+ Version: 0.2.69a6
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
@@ -176,7 +176,7 @@ Requires-Dist: types-pyyaml<7,>=6.0.12; extra == 'dev'
176
176
  Requires-Dist: types-requests<3,>=2.31.0; extra == 'dev'
177
177
  Requires-Dist: types-setuptools<70,>=69.2.0; extra == 'dev'
178
178
  Requires-Dist: types-tqdm<5,>=4.66.0; extra == 'dev'
179
- Requires-Dist: uv==0.6.5; extra == 'dev'
179
+ Requires-Dist: uv<0.8,>=0.7.0; extra == 'dev'
180
180
  Provides-Extra: dev-tools
181
181
  Requires-Dist: aci-sdk>=1.0.0b1; extra == 'dev-tools'
182
182
  Requires-Dist: agentops<0.4,>=0.3.21; extra == 'dev-tools'
@@ -322,10 +322,6 @@ Requires-Dist: pytidb-experimental==0.0.1.dev4; extra == 'storage'
322
322
  Requires-Dist: qdrant-client<2,>=1.9.0; extra == 'storage'
323
323
  Requires-Dist: redis<6,>=5.0.6; extra == 'storage'
324
324
  Requires-Dist: weaviate-client>=4.15.0; extra == 'storage'
325
- Provides-Extra: test
326
- Requires-Dist: mock<6,>=5; extra == 'test'
327
- Requires-Dist: pytest-asyncio<0.24,>=0.23.0; extra == 'test'
328
- Requires-Dist: pytest<8,>=7; extra == 'test'
329
325
  Provides-Extra: web-tools
330
326
  Requires-Dist: apify-client<2,>=1.8.1; extra == 'web-tools'
331
327
  Requires-Dist: beautifulsoup4<5,>=4; extra == 'web-tools'
@@ -1,4 +1,4 @@
1
- camel/__init__.py,sha256=Ryf5ZVEFpFI3TVgtC_wE9pClrhx8VR7g5d2M9lll1tU,901
1
+ camel/__init__.py,sha256=A5ziO5SRxLf3bwmM8EImvN3SUQiBmZraUQyvySPP66A,901
2
2
  camel/generators.py,sha256=JRqj9_m1PF4qT6UtybzTQ-KBT9MJQt18OAAYvQ_fr2o,13844
3
3
  camel/human.py,sha256=Xg8x1cS5KK4bQ1SDByiHZnzsRpvRP-KZViNvmu38xo4,5475
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=QFFyJOZ0fsyjjX_KwKsbp-SIFEFC6KTzMZ4dT98JsVg,80333
10
+ camel/agents/chat_agent.py,sha256=x0-PgQ2ptvqHYv_nu2tcWdWn7szl8o6TzYjr739U4Rk,79407
11
11
  camel/agents/critic_agent.py,sha256=L6cTbYjyZB0DCa51tQ6LZLA6my8kHLC4nktHySH78H4,10433
12
12
  camel/agents/deductive_reasoner_agent.py,sha256=6BZGaq1hR6hKJuQtOfoYQnk_AkZpw_Mr7mUy2MspQgs,13540
13
13
  camel/agents/embodied_agent.py,sha256=XBxBu5ZMmSJ4B2U3Z7SMwvLlgp6yNpaBe8HNQmY9CZA,7536
@@ -134,7 +134,7 @@ camel/interpreters/__init__.py,sha256=NOQUsg7gR84zO8nBXu4JGUatsxSDJqZS6otltjXfop
134
134
  camel/interpreters/base.py,sha256=J3DVt_dGzq1v09-gn8j2XRgEM2WoPrhKFyH9koAjkCU,2281
135
135
  camel/interpreters/docker_interpreter.py,sha256=IK26JFvqsM-7dOkT29qVqFujw5LF83_-BdSWsIplw4M,11318
136
136
  camel/interpreters/e2b_interpreter.py,sha256=lNtIsVJWdSdqLoILZS8f5_7WlHI3n457RzLjmeOqH-A,5319
137
- camel/interpreters/internal_python_interpreter.py,sha256=TU4ITQTsR8TXsklN11xWEcCkv4jnOVUmnBbHIHCKggk,23362
137
+ camel/interpreters/internal_python_interpreter.py,sha256=HjCIfB-HYv2cJz6nUg_8w53zHFB7DwSycqr7H22O_RI,24806
138
138
  camel/interpreters/interpreter_error.py,sha256=uEhcmHmmcajt5C9PLeHs21h1fE6cmyt23tCAGie1kTA,880
139
139
  camel/interpreters/ipython_interpreter.py,sha256=V-Z_nIwaKmmivv_gD6nwdzmqfBUW4IoN4vZ0IOaUTZU,6582
140
140
  camel/interpreters/subprocess_interpreter.py,sha256=DMLJIlTiHk0QA_pH5CXESHdJk-5olKo3aUh0KNECqJI,17759
@@ -178,7 +178,7 @@ camel/models/anthropic_model.py,sha256=m5J1-RabKyYf-2ejA7A8r4qbRLpS9oRtHWapzHs0B
178
178
  camel/models/aws_bedrock_model.py,sha256=bcQBiHVdVuJXyPEkSdymQlGx2zHIVj5d4ouFuF8CSY0,4670
179
179
  camel/models/azure_openai_model.py,sha256=FGQ6Cls-a7uzStsgPmBPffgm7TgTEzN3R2SWlAmiBeY,15164
180
180
  camel/models/base_audio_model.py,sha256=_VUWh1L3rh8mldNvM5R6jBOKtvmTeBKJyRxAdPJmPlY,3324
181
- camel/models/base_model.py,sha256=QKCgw16QVudV9nfaIGhyP2_73hu4FEdJHrF_wJO5mUc,14654
181
+ camel/models/base_model.py,sha256=VpPsxG5UDfEhidHsk5GP9wbahoOW4sMU-7-pAc1rqE0,17335
182
182
  camel/models/cohere_model.py,sha256=V1sL8JpaOAVYOi5hAIXWdEex0Mr36_kx4-BEACQfN5o,17237
183
183
  camel/models/crynux_model.py,sha256=leEiFz1RcRtVJLJvYt_Ydyg5jkSk3VEJphgmHFz67bw,4118
184
184
  camel/models/deepseek_model.py,sha256=AV7rKl0mYwiHsAifxOlekpFGtbc0LMhb7jnI5_hPzBE,10802
@@ -273,7 +273,7 @@ camel/societies/workforce/__init__.py,sha256=bkTI-PE-MSK9AQ2V2gR6cR2WY-R7Jqy_NmX
273
273
  camel/societies/workforce/base.py,sha256=z2DmbTP5LL5-aCAAqglznQqCLfPmnyM5zD3w6jjtsb8,2175
274
274
  camel/societies/workforce/prompts.py,sha256=_l7OUkzH5p7KOd8HMZle9zB9W3jKza_Yb_6elFKiZ2s,11813
275
275
  camel/societies/workforce/role_playing_worker.py,sha256=pWPCtkLx-xbc4SWyZBfMwvWr-R5ZpANhN7g6PDuH8Po,7615
276
- camel/societies/workforce/single_agent_worker.py,sha256=5lF9zzxDJMCtELh6Y407m_D2bMAXMsY3yvY3_Bssd5A,14704
276
+ camel/societies/workforce/single_agent_worker.py,sha256=ZDVq5doJUoUY0Uuvhkucf4U--y36q1We3p3c4TuoBSM,14627
277
277
  camel/societies/workforce/task_channel.py,sha256=uqQQI67Tr4awbR4bjZXdx8_4gL6-ON5IjQk_H_ryqT4,7431
278
278
  camel/societies/workforce/utils.py,sha256=Gjlz7pLo4r1b6iNHtlIMxeEuat4d6tEEQMI40JAU3kY,6190
279
279
  camel/societies/workforce/worker.py,sha256=36tkOyz4G2wfBdrFjt9NBPXsx4UbE6uL5on8sP2aoqk,6414
@@ -311,7 +311,7 @@ camel/terminators/__init__.py,sha256=t8uqrkUnXEOYMXQDgaBkMFJ0EXFKI0kmx4cUimli3Ls
311
311
  camel/terminators/base.py,sha256=xmJzERX7GdSXcxZjAHHODa0rOxRChMSRboDCNHWSscs,1511
312
312
  camel/terminators/response_terminator.py,sha256=n3G5KP6Oj7-7WlRN0yFcrtLpqAJKaKS0bmhrWlFfCgQ,4982
313
313
  camel/terminators/token_limit_terminator.py,sha256=YWv6ZR8R9yI2Qnf_3xES5bEE_O5bb2CxQ0EUXfMh34c,2118
314
- camel/toolkits/__init__.py,sha256=2Cf42WOw5v4kKo8WyoyHILoqtPdfV7_Xx4YuFcZt76o,5121
314
+ camel/toolkits/__init__.py,sha256=hgg25B07IzPdJWmDVm_XQzLRHjtiNwCaroJDfvAiDOQ,5356
315
315
  camel/toolkits/aci_toolkit.py,sha256=39AsXloBb16hHB7DKi6mFU6NPZ3iVpM2FZgaP4o4eLE,16060
316
316
  camel/toolkits/arxiv_toolkit.py,sha256=mw629nIN_ozaAxNv3nbvhonJKNI2-97ScRCBS3gVqNo,6297
317
317
  camel/toolkits/ask_news_toolkit.py,sha256=WfWaqwEo1Apbil3-Rb5y65Ws43NU4rAFWZu5VHe4los,23448
@@ -322,9 +322,11 @@ camel/toolkits/bohrium_toolkit.py,sha256=453t-m0h0IGjurG6tCHUejGzfRAN2SAkhIoY8V-
322
322
  camel/toolkits/browser_toolkit.py,sha256=Ntn_LmCrhqqIBWq9HtiIKw-M0cL5ebn74Ej1GBoZiC8,44400
323
323
  camel/toolkits/browser_toolkit_commons.py,sha256=uuc1V5tN3YJmTSe6NHAVJqwsL4iYD7IiSZWxPLYW67A,22196
324
324
  camel/toolkits/code_execution.py,sha256=ptexsx8t9teGse8ROnAHCYaLlDg843p-9p1ek-8JpWU,5620
325
+ camel/toolkits/craw4ai_toolkit.py,sha256=oJOjErSqMMpVKAtyjL8YmV9hnYl3gnZ2RReiOM4E5Rk,2665
325
326
  camel/toolkits/dalle_toolkit.py,sha256=GSnV7reQsVmhMi9sbQy1Ks_5Vs57Dlir_AbT2PPCZwo,6153
326
327
  camel/toolkits/dappier_toolkit.py,sha256=OEHOYXX_oXhgbVtWYAy13nO9uXf9i5qEXSwY4PexNFg,8194
327
328
  camel/toolkits/data_commons_toolkit.py,sha256=aHZUSL1ACpnYGaf1rE2csVKTmXTmN8lMGRUBYhZ_YEk,14168
329
+ camel/toolkits/edgeone_pages_mcp_toolkit.py,sha256=1TFpAGHUNLggFQeN1OEw7P5laijwnlrCkfxBtgxFuUY,2331
328
330
  camel/toolkits/excel_toolkit.py,sha256=DSjBXl24_LrJubGFFmB_vqliKzzWTbT1TH309YQVUO8,6653
329
331
  camel/toolkits/file_write_toolkit.py,sha256=i1CivjvqTpHakYK_EynNvNikjZZYsusxkjT2v-f9EYc,16473
330
332
  camel/toolkits/function_tool.py,sha256=xCDzjxTRCVj_kmbnMFBuwK-3NuzM4JNe_kv9HLtjnIA,33644
@@ -337,6 +339,7 @@ camel/toolkits/image_analysis_toolkit.py,sha256=IC36mtytgyMSW20IImIZDgQPsgPxmxZG
337
339
  camel/toolkits/jina_reranker_toolkit.py,sha256=0OWUlSqRNYYmD5EQZW7OX87lfmzLRjjDmHRyqHU6dmU,11963
338
340
  camel/toolkits/klavis_toolkit.py,sha256=ZKerhgz5e-AV-iv0ftf07HgWikknIHjB3EOQswfuR80,9864
339
341
  camel/toolkits/linkedin_toolkit.py,sha256=wn4eXwYYlVA7doTna7k7WYhUqTBF83W79S-UJs_IQr0,8065
342
+ camel/toolkits/markitdown_toolkit.py,sha256=7jxPbaE2CIq1FpT41q52Fznw-Gx05ITWn1t9O5IytA4,2867
340
343
  camel/toolkits/math_toolkit.py,sha256=KdI8AJ9Dbq5cfWboAYJUYgSkmADMCO5eZ6yqYkWuiIQ,3686
341
344
  camel/toolkits/mcp_toolkit.py,sha256=czGYgZ-Vd9NdAcPe_hoA7ZYxSV2QKhsxuJNZNg3Rof4,25459
342
345
  camel/toolkits/memory_toolkit.py,sha256=TeKYd5UMwgjVpuS2orb-ocFL13eUNKujvrFOruDCpm8,4436
@@ -374,7 +377,7 @@ camel/toolkits/zapier_toolkit.py,sha256=A83y1UcfuopH7Fx82pORzypl1StbhBjB2HhyOqYa
374
377
  camel/toolkits/non_visual_browser_toolkit/__init__.py,sha256=mXAjf6qkl8BgJrBKPR5d2C3B2m4pOHvq6sRavmgOweA,812
375
378
  camel/toolkits/non_visual_browser_toolkit/actions.py,sha256=pue7Q5ZGLhqcmbkuahrN_JBYdeV0xtqu9i4gr9mqGWU,7113
376
379
  camel/toolkits/non_visual_browser_toolkit/agent.py,sha256=y6sAWlPXScVn7f2bPueS8yd8IQ3CkJ0HutP4DChs6P8,10330
377
- camel/toolkits/non_visual_browser_toolkit/browser_non_visual_toolkit.py,sha256=YM1mxicA56tdd8qt3WUJMCPjruIzRCvYRZA_q7tC4qk,14002
380
+ camel/toolkits/non_visual_browser_toolkit/browser_non_visual_toolkit.py,sha256=GsaJ9_8M9n7D-SJIwC2TeTL5Yon76ueTWikiMZ7LwHE,15999
378
381
  camel/toolkits/non_visual_browser_toolkit/nv_browser_session.py,sha256=ylN5DgZzgG7biV99ufyFBfjGf0cMlrtzX-3wO409EI4,7304
379
382
  camel/toolkits/non_visual_browser_toolkit/snapshot.js,sha256=4MHtRGlbvJWxs1gQC9RS0ButppPeLZPIHTwZ7AS7hFQ,6627
380
383
  camel/toolkits/non_visual_browser_toolkit/snapshot.py,sha256=jnnuIm1VqeoTAAw96kKOZum17-mwghPsm0uRSFoYvxU,7708
@@ -431,7 +434,7 @@ camel/verifiers/math_verifier.py,sha256=tA1D4S0sm8nsWISevxSN0hvSVtIUpqmJhzqfbuMo
431
434
  camel/verifiers/models.py,sha256=GdxYPr7UxNrR1577yW4kyroRcLGfd-H1GXgv8potDWU,2471
432
435
  camel/verifiers/physics_verifier.py,sha256=c1grrRddcrVN7szkxhv2QirwY9viIRSITWeWFF5HmLs,30187
433
436
  camel/verifiers/python_verifier.py,sha256=ogTz77wODfEcDN4tMVtiSkRQyoiZbHPY2fKybn59lHw,20558
434
- camel_ai-0.2.69a3.dist-info/METADATA,sha256=IqaSVunoJDMvh0Hp12M5MItps4OkEnqbmPx4NVi_xZw,45047
435
- camel_ai-0.2.69a3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
436
- camel_ai-0.2.69a3.dist-info/licenses/LICENSE,sha256=id0nB2my5kG0xXeimIu5zZrbHLS6EQvxvkKkzIHaT2k,11343
437
- camel_ai-0.2.69a3.dist-info/RECORD,,
437
+ camel_ai-0.2.69a6.dist-info/METADATA,sha256=LkkXRk2a3WfankzmWhW2GWnrWQzTA6gNgGpAnq50fvA,44882
438
+ camel_ai-0.2.69a6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
439
+ camel_ai-0.2.69a6.dist-info/licenses/LICENSE,sha256=id0nB2my5kG0xXeimIu5zZrbHLS6EQvxvkKkzIHaT2k,11343
440
+ camel_ai-0.2.69a6.dist-info/RECORD,,