camel-ai 0.2.76a1__py3-none-any.whl → 0.2.76a3__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.76a1'
17
+ __version__ = '0.2.76a3'
18
18
 
19
19
  __all__ = [
20
20
  '__version__',
@@ -483,7 +483,7 @@ class TicTacToeEnv(MultiStepEnv):
483
483
  # Check all win combinations.
484
484
  for a, b, c in TicTacToeEnv.WIN_COMBINATIONS:
485
485
  if board[a] != " " and board[a] == board[b] == board[c]:
486
- return board[a]
486
+ return board[a] # type: ignore[return-value]
487
487
  # Check for draw.
488
488
  if all(cell != " " for cell in board):
489
489
  return "draw"
@@ -24,6 +24,7 @@ from openai.lib.streaming.chat import (
24
24
  )
25
25
  from pydantic import BaseModel
26
26
 
27
+ from camel.logger import get_logger as camel_get_logger
27
28
  from camel.messages import OpenAIMessage
28
29
  from camel.types import (
29
30
  ChatCompletion,
@@ -34,6 +35,21 @@ from camel.types import (
34
35
  )
35
36
  from camel.utils import BaseTokenCounter
36
37
 
38
+ if os.environ.get("TRACEROOT_ENABLED", "False").lower() == "true":
39
+ try:
40
+ from traceroot import get_logger # type: ignore[import]
41
+ from traceroot import trace as observe # type: ignore[import]
42
+
43
+ logger = get_logger('base_model')
44
+ except ImportError:
45
+ from camel.utils import observe
46
+
47
+ logger = camel_get_logger('base_model')
48
+ else:
49
+ from camel.utils import observe
50
+
51
+ logger = camel_get_logger('base_model')
52
+
37
53
 
38
54
  class ModelBackendMeta(abc.ABCMeta):
39
55
  r"""Metaclass that automatically preprocesses messages in run method.
@@ -364,6 +380,7 @@ class BaseModelBackend(ABC, metaclass=ModelBackendMeta):
364
380
  """
365
381
  pass
366
382
 
383
+ @observe()
367
384
  def run(
368
385
  self,
369
386
  messages: List[OpenAIMessage],
@@ -403,7 +420,13 @@ class BaseModelBackend(ABC, metaclass=ModelBackendMeta):
403
420
  elif not tools:
404
421
  tools = None
405
422
 
423
+ logger.info("Running model: %s", self.model_type)
424
+ logger.info("Messages: %s", messages)
425
+ logger.info("Response format: %s", response_format)
426
+ logger.info("Tools: %s", tools)
427
+
406
428
  result = self._run(messages, response_format, tools)
429
+ logger.info("Result: %s", result)
407
430
 
408
431
  # Log the response if logging is enabled
409
432
  if log_path:
@@ -411,6 +434,7 @@ class BaseModelBackend(ABC, metaclass=ModelBackendMeta):
411
434
 
412
435
  return result
413
436
 
437
+ @observe()
414
438
  async def arun(
415
439
  self,
416
440
  messages: List[OpenAIMessage],
@@ -448,7 +472,13 @@ class BaseModelBackend(ABC, metaclass=ModelBackendMeta):
448
472
  elif not tools:
449
473
  tools = None
450
474
 
475
+ logger.info("Running model: %s", self.model_type)
476
+ logger.info("Messages: %s", messages)
477
+ logger.info("Response format: %s", response_format)
478
+ logger.info("Tools: %s", tools)
479
+
451
480
  result = await self._arun(messages, response_format, tools)
481
+ logger.info("Result: %s", result)
452
482
 
453
483
  # Log the response if logging is enabled
454
484
  if log_path:
@@ -16,6 +16,7 @@ from __future__ import annotations
16
16
  import asyncio
17
17
  import concurrent.futures
18
18
  import json
19
+ import os
19
20
  import time
20
21
  import uuid
21
22
  from collections import deque
@@ -79,7 +80,15 @@ from camel.utils import dependencies_required
79
80
 
80
81
  from .workforce_logger import WorkforceLogger
81
82
 
82
- logger = get_logger(__name__)
83
+ if os.environ.get("TRACEROOT_ENABLED", "False").lower() == "true":
84
+ try:
85
+ import traceroot # type: ignore[import]
86
+
87
+ logger = traceroot.get_logger('camel')
88
+ except ImportError:
89
+ logger = get_logger(__name__)
90
+ else:
91
+ logger = get_logger(__name__)
83
92
 
84
93
  # Constants for configuration values
85
94
  MAX_TASK_RETRIES = 3
@@ -25,6 +25,66 @@ class HybridBrowserToolkit(BaseToolkit):
25
25
  This wrapper allows users to choose between:
26
26
  - 'typescript': WebSocket-based implementation using TypeScript/Node.js
27
27
  - 'python': Pure Python implementation using Playwright directly
28
+
29
+ Args:
30
+ mode (Literal["typescript", "python"]): Implementation mode. -
31
+ 'typescript': Uses WebSocket-based TypeScript implementation -
32
+ 'python': Uses pure Python Playwright implementation. Defaults to
33
+ "typescript".
34
+ headless (bool): Whether to run browser in headless mode.
35
+ Defaults to True.
36
+ user_data_dir (Optional[str]): Directory for user data
37
+ persistence. Defaults to None.
38
+ stealth (bool): Whether to enable stealth mode. Defaults to
39
+ False.
40
+ web_agent_model (Optional[BaseModelBackend]): Model for web
41
+ agent operations. Defaults to None.
42
+ cache_dir (str): Directory for caching. Defaults to "tmp/".
43
+ enabled_tools (Optional[List[str]]): List of enabled tools.
44
+ Defaults to None.
45
+ browser_log_to_file (bool): Whether to log browser actions to
46
+ file. Defaults to False.
47
+ log_dir (Optional[str]): Custom directory path for log files.
48
+ If None, defaults to "browser_log". Defaults to None.
49
+ session_id (Optional[str]): Session identifier. Defaults to None.
50
+ default_start_url (str): Default URL to start with. Defaults
51
+ to "https://google.com/".
52
+ default_timeout (Optional[int]): Default timeout in
53
+ milliseconds. Defaults to None.
54
+ short_timeout (Optional[int]): Short timeout in milliseconds.
55
+ Defaults to None.
56
+ navigation_timeout (Optional[int]): Navigation timeout in
57
+ milliseconds. Defaults to None.
58
+ network_idle_timeout (Optional[int]): Network idle timeout in
59
+ milliseconds. Defaults to None.
60
+ screenshot_timeout (Optional[int]): Screenshot timeout in
61
+ milliseconds. Defaults to None.
62
+ page_stability_timeout (Optional[int]): Page stability timeout
63
+ in milliseconds. Defaults to None.
64
+ dom_content_loaded_timeout (Optional[int]): DOM content loaded
65
+ timeout in milliseconds. Defaults to None.
66
+ viewport_limit (bool): Whether to filter page snapshot
67
+ elements to only those visible in the current viewport.
68
+ Defaults to False.
69
+ connect_over_cdp (bool): Whether to connect to an existing
70
+ browser via Chrome DevTools Protocol. Defaults to False.
71
+ (Only supported in TypeScript mode)
72
+ cdp_url (Optional[str]): WebSocket endpoint URL for CDP
73
+ connection. Required when connect_over_cdp is True.
74
+ Defaults to None. (Only supported in TypeScript mode)
75
+ cdp_keep_current_page (bool): When True and using CDP mode,
76
+ won't create new pages but use the existing one. Defaults to False.
77
+ (Only supported in TypeScript mode)
78
+ full_visual_mode (bool): When True, browser actions like click,
79
+ browser_open, visit_page, etc. will return 'full visual mode'
80
+ as snapshot instead of actual page content. The
81
+ browser_get_page_snapshot method will still return the actual
82
+ snapshot. Defaults to False.
83
+ **kwargs: Additional keyword arguments passed to the
84
+ implementation.
85
+
86
+ Returns:
87
+ HybridBrowserToolkit instance of the specified implementation.
28
88
  """
29
89
 
30
90
  def __new__(
@@ -109,6 +109,7 @@ class WebSocketBrowserWrapper:
109
109
  self._send_lock = asyncio.Lock()
110
110
  self._receive_task = None
111
111
  self._pending_responses: Dict[str, asyncio.Future[Dict[str, Any]]] = {}
112
+ self._browser_opened = False
112
113
  self._server_ready_future = None
113
114
 
114
115
  self.browser_log_to_file = (config or {}).get(
@@ -191,10 +192,14 @@ class WebSocketBrowserWrapper:
191
192
  """Start the WebSocket server and connect to it."""
192
193
  await self._cleanup_existing_processes()
193
194
 
195
+ import platform
196
+
197
+ use_shell = platform.system() == 'Windows'
194
198
  npm_check = subprocess.run(
195
199
  ['npm', '--version'],
196
200
  capture_output=True,
197
201
  text=True,
202
+ shell=use_shell,
198
203
  )
199
204
  if npm_check.returncode != 0:
200
205
  raise RuntimeError(
@@ -207,6 +212,7 @@ class WebSocketBrowserWrapper:
207
212
  ['node', '--version'],
208
213
  capture_output=True,
209
214
  text=True,
215
+ shell=use_shell,
210
216
  )
211
217
  if node_check.returncode != 0:
212
218
  raise RuntimeError(
@@ -223,6 +229,7 @@ class WebSocketBrowserWrapper:
223
229
  cwd=self.ts_dir,
224
230
  capture_output=True,
225
231
  text=True,
232
+ shell=use_shell,
226
233
  )
227
234
  if install_result.returncode != 0:
228
235
  logger.error(f"npm install failed: {install_result.stderr}")
@@ -237,6 +244,7 @@ class WebSocketBrowserWrapper:
237
244
  cwd=self.ts_dir,
238
245
  capture_output=True,
239
246
  text=True,
247
+ shell=use_shell,
240
248
  )
241
249
  if build_result.returncode != 0:
242
250
  logger.error(f"TypeScript build failed: {build_result.stderr}")
@@ -244,13 +252,16 @@ class WebSocketBrowserWrapper:
244
252
  f"TypeScript build failed: {build_result.stderr}"
245
253
  )
246
254
 
255
+ # use_shell already defined above
247
256
  self.process = subprocess.Popen(
248
257
  ['node', 'websocket-server.js'],
249
258
  cwd=self.ts_dir,
250
259
  stdout=subprocess.PIPE,
251
260
  stderr=subprocess.STDOUT,
252
261
  text=True,
262
+ encoding='utf-8',
253
263
  bufsize=1,
264
+ shell=use_shell,
254
265
  )
255
266
 
256
267
  self._server_ready_future = asyncio.get_running_loop().create_future()
@@ -391,6 +402,9 @@ class WebSocketBrowserWrapper:
391
402
 
392
403
  await self._send_command('init', self.config)
393
404
 
405
+ if self.config.get('cdpUrl'):
406
+ self._browser_opened = True
407
+
394
408
  async def stop(self):
395
409
  """Stop the WebSocket connection and server."""
396
410
  if self.websocket:
@@ -400,11 +414,12 @@ class WebSocketBrowserWrapper:
400
414
  timeout=2.0,
401
415
  )
402
416
 
403
- # Close websocket connection
404
417
  with contextlib.suppress(Exception):
405
418
  await self.websocket.close()
406
419
  self.websocket = None
407
420
 
421
+ self._browser_opened = False
422
+
408
423
  # Gracefully stop the Node process before cancelling the log reader
409
424
  if self.process:
410
425
  try:
@@ -448,12 +463,13 @@ class WebSocketBrowserWrapper:
448
463
 
449
464
  This is useful for CDP mode where the browser should remain open.
450
465
  """
451
- # Close websocket connection
452
466
  if self.websocket:
453
467
  with contextlib.suppress(Exception):
454
468
  await self.websocket.close()
455
469
  self.websocket = None
456
470
 
471
+ self._browser_opened = False
472
+
457
473
  # Stop the Node process
458
474
  if self.process:
459
475
  try:
@@ -709,17 +725,31 @@ class WebSocketBrowserWrapper:
709
725
  response = await self._send_command(
710
726
  'open_browser', {'startUrl': start_url}
711
727
  )
728
+ self._browser_opened = True
712
729
  return response
713
730
 
714
731
  @action_logger
715
732
  async def close_browser(self) -> str:
716
733
  """Close browser."""
717
734
  response = await self._send_command('close_browser', {})
735
+ self._browser_opened = False
718
736
  return response['message']
719
737
 
720
738
  @action_logger
721
739
  async def visit_page(self, url: str) -> Dict[str, Any]:
722
- """Visit a page."""
740
+ """Visit a page.
741
+
742
+ In non-CDP mode, automatically opens browser if not already open.
743
+ """
744
+ if not self._browser_opened:
745
+ is_cdp_mode = bool(self.config.get('cdpUrl'))
746
+
747
+ if not is_cdp_mode:
748
+ logger.info(
749
+ "Browser not open, automatically opening browser..."
750
+ )
751
+ await self.open_browser()
752
+
723
753
  response = await self._send_command('visit_page', {'url': url})
724
754
  return response
725
755
 
@@ -18,6 +18,8 @@ import warnings
18
18
  from contextlib import AsyncExitStack
19
19
  from typing import Any, Dict, List, Optional
20
20
 
21
+ from typing_extensions import TypeGuard
22
+
21
23
  from camel.logger import get_logger
22
24
  from camel.toolkits import BaseToolkit, FunctionTool
23
25
  from camel.utils.commons import run_async
@@ -43,6 +45,187 @@ class MCPToolError(Exception):
43
45
  pass
44
46
 
45
47
 
48
+ _EMPTY_SCHEMA = {
49
+ "additionalProperties": False,
50
+ "type": "object",
51
+ "properties": {},
52
+ "required": [],
53
+ }
54
+
55
+
56
+ def ensure_strict_json_schema(schema: dict[str, Any]) -> dict[str, Any]:
57
+ r"""Mutates the given JSON schema to ensure it conforms to the
58
+ `strict` standard that the OpenAI API expects.
59
+ """
60
+ if schema == {}:
61
+ return _EMPTY_SCHEMA
62
+ return _ensure_strict_json_schema(schema, path=(), root=schema)
63
+
64
+
65
+ def _ensure_strict_json_schema(
66
+ json_schema: object,
67
+ *,
68
+ path: tuple[str, ...],
69
+ root: dict[str, object],
70
+ ) -> dict[str, Any]:
71
+ if not is_dict(json_schema):
72
+ raise TypeError(
73
+ f"Expected {json_schema} to be a dictionary; path={path}"
74
+ )
75
+
76
+ defs = json_schema.get("$defs")
77
+ if is_dict(defs):
78
+ for def_name, def_schema in defs.items():
79
+ _ensure_strict_json_schema(
80
+ def_schema, path=(*path, "$defs", def_name), root=root
81
+ )
82
+
83
+ definitions = json_schema.get("definitions")
84
+ if is_dict(definitions):
85
+ for definition_name, definition_schema in definitions.items():
86
+ _ensure_strict_json_schema(
87
+ definition_schema,
88
+ path=(*path, "definitions", definition_name),
89
+ root=root,
90
+ )
91
+
92
+ typ = json_schema.get("type")
93
+ if typ == "object" and "additionalProperties" not in json_schema:
94
+ json_schema["additionalProperties"] = False
95
+ elif (
96
+ typ == "object"
97
+ and "additionalProperties" in json_schema
98
+ and json_schema["additionalProperties"]
99
+ ):
100
+ raise ValueError(
101
+ "additionalProperties should not be set for object types. This "
102
+ "could be because you're using an older version of Pydantic, or "
103
+ "because you configured additional properties to be allowed. If "
104
+ "you really need this, update the function or output tool "
105
+ "to not use a strict schema."
106
+ )
107
+
108
+ # object types
109
+ # { 'type': 'object', 'properties': { 'a': {...} } }
110
+ properties = json_schema.get("properties")
111
+ if is_dict(properties):
112
+ json_schema["required"] = list(properties.keys())
113
+ json_schema["properties"] = {
114
+ key: _ensure_strict_json_schema(
115
+ prop_schema, path=(*path, "properties", key), root=root
116
+ )
117
+ for key, prop_schema in properties.items()
118
+ }
119
+
120
+ # arrays
121
+ # { 'type': 'array', 'items': {...} }
122
+ items = json_schema.get("items")
123
+ if is_dict(items):
124
+ json_schema["items"] = _ensure_strict_json_schema(
125
+ items, path=(*path, "items"), root=root
126
+ )
127
+
128
+ # unions
129
+ any_of = json_schema.get("anyOf")
130
+ if is_list(any_of):
131
+ json_schema["anyOf"] = [
132
+ _ensure_strict_json_schema(
133
+ variant, path=(*path, "anyOf", str(i)), root=root
134
+ )
135
+ for i, variant in enumerate(any_of)
136
+ ]
137
+
138
+ # intersections
139
+ all_of = json_schema.get("allOf")
140
+ if is_list(all_of):
141
+ if len(all_of) == 1:
142
+ json_schema.update(
143
+ _ensure_strict_json_schema(
144
+ all_of[0], path=(*path, "allOf", "0"), root=root
145
+ )
146
+ )
147
+ json_schema.pop("allOf")
148
+ else:
149
+ json_schema["allOf"] = [
150
+ _ensure_strict_json_schema(
151
+ entry, path=(*path, "allOf", str(i)), root=root
152
+ )
153
+ for i, entry in enumerate(all_of)
154
+ ]
155
+
156
+ # strip `None` defaults as there's no meaningful distinction here
157
+ # the schema will still be `nullable` and the model will default
158
+ # to using `None` anyway
159
+ if json_schema.get("default", None) is None:
160
+ json_schema.pop("default", None)
161
+
162
+ # we can't use `$ref`s if there are also other properties defined, e.g.
163
+ # `{"$ref": "...", "description": "my description"}`
164
+ #
165
+ # so we unravel the ref
166
+ # `{"type": "string", "description": "my description"}`
167
+ ref = json_schema.get("$ref")
168
+ if ref and has_more_than_n_keys(json_schema, 1):
169
+ assert isinstance(ref, str), f"Received non-string $ref - {ref}"
170
+
171
+ resolved = resolve_ref(root=root, ref=ref)
172
+ if not is_dict(resolved):
173
+ raise ValueError(
174
+ f"Expected `$ref: {ref}` to resolved to a dictionary but got "
175
+ f"{resolved}"
176
+ )
177
+
178
+ # properties from the json schema take priority
179
+ # over the ones on the `$ref`
180
+ json_schema.update({**resolved, **json_schema})
181
+ json_schema.pop("$ref")
182
+ # Since the schema expanded from `$ref` might not
183
+ # have `additionalProperties: false` applied
184
+ # we call `_ensure_strict_json_schema` again to fix the inlined
185
+ # schema and ensure it's valid
186
+ return _ensure_strict_json_schema(json_schema, path=path, root=root)
187
+
188
+ return json_schema
189
+
190
+
191
+ def resolve_ref(*, root: dict[str, object], ref: str) -> object:
192
+ if not ref.startswith("#/"):
193
+ raise ValueError(
194
+ f"Unexpected $ref format {ref!r}; Does not start with #/"
195
+ )
196
+
197
+ path = ref[2:].split("/")
198
+ resolved = root
199
+ for key in path:
200
+ value = resolved[key]
201
+ assert is_dict(value), (
202
+ f"encountered non-dictionary entry while resolving {ref} - "
203
+ f"{resolved}"
204
+ )
205
+ resolved = value
206
+
207
+ return resolved
208
+
209
+
210
+ def is_dict(obj: object) -> TypeGuard[dict[str, object]]:
211
+ # just pretend that we know there are only `str` keys
212
+ # as that check is not worth the performance cost
213
+ return isinstance(obj, dict)
214
+
215
+
216
+ def is_list(obj: object) -> TypeGuard[list[object]]:
217
+ return isinstance(obj, list)
218
+
219
+
220
+ def has_more_than_n_keys(obj: dict[str, object], n: int) -> bool:
221
+ i = 0
222
+ for _ in obj.keys():
223
+ i += 1
224
+ if i > n:
225
+ return True
226
+ return False
227
+
228
+
46
229
  class MCPToolkit(BaseToolkit):
47
230
  r"""MCPToolkit provides a unified interface for managing multiple
48
231
  MCP server connections and their tools.
@@ -476,367 +659,149 @@ class MCPToolkit(BaseToolkit):
476
659
  raise ValueError(error_msg) from e
477
660
 
478
661
  def _ensure_strict_tool_schema(self, tool: FunctionTool) -> FunctionTool:
479
- r"""Ensure a tool has a strict schema compatible with OpenAI's
480
- requirements according to the structured outputs specification.
481
-
482
- Args:
483
- tool (FunctionTool): The tool to check and update if necessary.
484
-
485
- Returns:
486
- FunctionTool: The tool with a strict schema.
662
+ r"""Ensure a tool has a strict schema compatible with
663
+ OpenAI's requirements.
664
+
665
+ Strategy:
666
+ - Ensure parameters exist with at least an empty properties object
667
+ (OpenAI requirement).
668
+ - Try converting parameters to strict using ensure_strict_json_schema.
669
+ - If conversion fails, mark function.strict = False and
670
+ keep best-effort parameters.
487
671
  """
488
672
  try:
489
673
  schema = tool.get_openai_tool_schema()
490
674
 
491
- # Helper functions for validation and transformation
492
- def _validate_and_fix_schema(obj, path="", in_root=True):
493
- r"""Recursively validate and fix schema to meet strict
494
- requirements.
495
- """
496
- if isinstance(obj, dict):
497
- # Check if this is the root object
498
- if in_root and path == "":
499
- # Root must be an object, not anyOf
500
- if "anyOf" in obj and "type" not in obj:
501
- raise ValueError(
502
- "Root object must not be anyOf and must "
503
- "be an object"
504
- )
505
- if obj.get("type") and obj["type"] != "object":
506
- raise ValueError(
507
- "Root object must have type 'object'"
508
- )
509
-
510
- # Handle object types
511
- if obj.get("type") == "object":
512
- # Ensure additionalProperties is false
513
- obj["additionalProperties"] = False
514
-
515
- # Process properties
516
- if "properties" in obj:
517
- props = obj["properties"]
518
- # Only set required if it doesn't exist or needs
519
- # updating
520
- if "required" not in obj:
521
- # If no required field exists, make all fields
522
- # required
523
- obj["required"] = list(props.keys())
524
- else:
525
- # Ensure required field only contains valid
526
- # property names
527
- existing_required = obj.get("required", [])
528
- valid_required = [
529
- req
530
- for req in existing_required
531
- if req in props
532
- ]
533
- # Add any missing properties to required
534
- for prop_name in props:
535
- if prop_name not in valid_required:
536
- valid_required.append(prop_name)
537
- obj["required"] = valid_required
538
-
539
- # Recursively process each property
540
- for prop_name, prop_schema in props.items():
541
- _validate_and_fix_schema(
542
- prop_schema, f"{path}.{prop_name}", False
543
- )
544
-
545
- # Handle arrays
546
- elif obj.get("type") == "array":
547
- if "items" in obj:
548
- _validate_and_fix_schema(
549
- obj["items"], f"{path}.items", False
550
- )
675
+ def _has_strict_mode_incompatible_features(json_schema):
676
+ r"""Check if schema has features incompatible
677
+ with OpenAI strict mode."""
551
678
 
552
- # Handle anyOf
553
- elif "anyOf" in obj:
554
- # Validate anyOf schemas
555
- for i, schema in enumerate(obj["anyOf"]):
556
- _validate_and_fix_schema(
557
- schema, f"{path}.anyOf[{i}]", False
558
- )
559
-
560
- # Handle string format validation
561
- elif obj.get("type") == "string":
562
- if "format" in obj:
563
- allowed_formats = [
564
- "date-time",
565
- "time",
566
- "date",
567
- "duration",
568
- "email",
569
- "hostname",
570
- "ipv4",
571
- "ipv6",
572
- "uuid",
573
- ]
574
- if obj["format"] not in allowed_formats:
575
- del obj["format"] # Remove unsupported format
576
-
577
- # Handle number/integer validation
578
- elif obj.get("type") in ["number", "integer"]:
579
- # These properties are supported
580
- supported_props = [
581
- "multipleOf",
582
- "maximum",
583
- "exclusiveMaximum",
584
- "minimum",
585
- "exclusiveMinimum",
586
- ]
587
- # Remove any unsupported properties
588
- for key in list(obj.keys()):
589
- if key not in [
590
- *supported_props,
591
- "type",
592
- "description",
593
- "default",
594
- ]:
595
- del obj[key]
596
-
597
- # Process nested structures
598
- for key in ["allOf", "oneOf", "$defs", "definitions"]:
599
- if key in obj:
600
- if isinstance(obj[key], list):
601
- for i, item in enumerate(obj[key]):
602
- _validate_and_fix_schema(
603
- item, f"{path}.{key}[{i}]", False
604
- )
605
- elif isinstance(obj[key], dict):
606
- for def_name, def_schema in obj[key].items():
607
- _validate_and_fix_schema(
608
- def_schema,
609
- f"{path}.{key}.{def_name}",
610
- False,
611
- )
612
-
613
- elif isinstance(obj, list):
614
- for i, item in enumerate(obj):
615
- _validate_and_fix_schema(item, f"{path}[{i}]", False)
616
-
617
- def _check_schema_limits(obj, counts=None):
618
- r"""Check if schema exceeds OpenAI limits."""
619
- if counts is None:
620
- counts = {
621
- "properties": 0,
622
- "depth": 0,
623
- "enums": 0,
624
- "string_length": 0,
625
- }
679
+ def _check_incompatible(obj, path=""):
680
+ if not isinstance(obj, dict):
681
+ return False
626
682
 
627
- def _count_properties(o, depth=0):
628
- if isinstance(o, dict):
629
- if depth > 5:
630
- raise ValueError(
631
- "Schema exceeds maximum nesting depth of 5"
683
+ # Check for allOf in array items (known to cause issues)
684
+ if "items" in obj and isinstance(obj["items"], dict):
685
+ items_schema = obj["items"]
686
+ if "allOf" in items_schema:
687
+ logger.debug(
688
+ f"Found allOf in array items at {path}"
632
689
  )
690
+ return True
691
+ # Recursively check items schema
692
+ if _check_incompatible(items_schema, f"{path}.items"):
693
+ return True
694
+
695
+ # Check for other potentially problematic patterns
696
+ # anyOf/oneOf in certain contexts can also cause issues
697
+ if (
698
+ "anyOf" in obj and len(obj["anyOf"]) > 10
699
+ ): # Large unions can be problematic
700
+ return True
701
+
702
+ # Recursively check nested objects
703
+ for key in [
704
+ "properties",
705
+ "additionalProperties",
706
+ "patternProperties",
707
+ ]:
708
+ if key in obj and isinstance(obj[key], dict):
709
+ if key == "properties":
710
+ for prop_name, prop_schema in obj[key].items():
711
+ if isinstance(
712
+ prop_schema, dict
713
+ ) and _check_incompatible(
714
+ prop_schema,
715
+ f"{path}.{key}.{prop_name}",
716
+ ):
717
+ return True
718
+ elif _check_incompatible(
719
+ obj[key], f"{path}.{key}"
720
+ ):
721
+ return True
633
722
 
634
- if o.get("type") == "object" and "properties" in o:
635
- counts["properties"] += len(o["properties"])
636
- for prop in o["properties"].values():
637
- _count_properties(prop, depth + 1)
638
-
639
- if "enum" in o:
640
- counts["enums"] += len(o["enum"])
641
- if isinstance(o["enum"], list):
642
- for val in o["enum"]:
643
- if isinstance(val, str):
644
- counts["string_length"] += len(val)
645
-
646
- # Count property names
647
- if "properties" in o:
648
- for name in o["properties"].keys():
649
- counts["string_length"] += len(name)
650
-
651
- # Process nested structures
652
- for key in ["items", "allOf", "oneOf", "anyOf"]:
653
- if key in o:
654
- if isinstance(o[key], dict):
655
- _count_properties(o[key], depth)
656
- elif isinstance(o[key], list):
657
- for item in o[key]:
658
- _count_properties(item, depth)
659
-
660
- _count_properties(obj)
661
-
662
- # Check limits, reference: https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#objects-have-limitations-on-nesting-depth-and-size # noqa: E501
663
- if counts["properties"] > 5000:
664
- raise ValueError(
665
- "Schema exceeds maximum of 5000 properties"
666
- )
667
- if counts["enums"] > 1000:
668
- raise ValueError(
669
- "Schema exceeds maximum of 1000 enum values"
670
- )
671
- if counts["string_length"] > 120000:
672
- raise ValueError(
673
- "Schema exceeds maximum total string length of 120000"
674
- )
675
-
676
- return True
723
+ # Check arrays and unions
724
+ for key in ["allOf", "anyOf", "oneOf"]:
725
+ if key in obj and isinstance(obj[key], list):
726
+ for i, item in enumerate(obj[key]):
727
+ if isinstance(
728
+ item, dict
729
+ ) and _check_incompatible(
730
+ item, f"{path}.{key}[{i}]"
731
+ ):
732
+ return True
677
733
 
678
- # Check if schema has any issues that prevent strict mode
679
- def _has_strict_mode_issues(obj):
680
- r"""Check for any issues that would prevent strict mode."""
681
- issues = []
734
+ return False
682
735
 
683
- def _check_issues(o, path=""):
684
- if isinstance(o, dict):
685
- # Check for additionalProperties: true
686
- if o.get("additionalProperties") is True:
687
- issues.append(
688
- f"additionalProperties: true at {path}"
689
- )
736
+ return _check_incompatible(json_schema)
690
737
 
691
- # Check for unsupported keywords
692
- unsupported = [
693
- "not",
694
- "dependentRequired",
695
- "dependentSchemas",
696
- "if",
697
- "then",
698
- "else",
699
- "patternProperties",
700
- ]
701
- for keyword in unsupported:
702
- if keyword in o:
703
- issues.append(
704
- f"Unsupported keyword '{keyword}' "
705
- f"at {path}"
706
- )
707
-
708
- # Recursively check
709
- for key, value in o.items():
710
- if isinstance(value, (dict, list)):
711
- _check_issues(value, f"{path}.{key}")
712
-
713
- elif isinstance(o, list):
714
- for i, item in enumerate(o):
715
- _check_issues(item, f"{path}[{i}]")
716
-
717
- _check_issues(obj)
718
- return issues
719
-
720
- # Check if already strict and compliant
721
- if schema.get("function", {}).get("strict") is True:
722
- # Validate it's actually compliant
723
- try:
724
- params = schema["function"].get("parameters", {})
725
- if params:
726
- _validate_and_fix_schema(params)
727
- _check_schema_limits(params)
728
- return tool
729
- except Exception:
730
- # Not actually compliant, continue to fix it
731
- pass
732
-
733
- # Apply sanitization first to handle optional fields properly
738
+ # Apply sanitization if available
734
739
  if "function" in schema:
735
- # Apply the sanitization function first
736
- from camel.toolkits.function_tool import (
737
- sanitize_and_enforce_required,
738
- )
740
+ try:
741
+ from camel.toolkits.function_tool import (
742
+ sanitize_and_enforce_required,
743
+ )
739
744
 
740
- schema = sanitize_and_enforce_required(schema)
741
-
742
- # Special handling for schemas with additionalProperties that
743
- # aren't false These can't use strict mode
744
- def _has_open_props(obj, path=""):
745
- """Check if any object has additionalProperties that
746
- isn't false."""
747
- if isinstance(obj, dict):
748
- if (
749
- obj.get("type") == "object"
750
- and "additionalProperties" in obj
751
- ):
752
- if obj["additionalProperties"] is not False:
753
- return True
745
+ schema = sanitize_and_enforce_required(schema)
746
+ except ImportError:
747
+ logger.debug("sanitize_and_enforce_required not available")
754
748
 
755
- # Recurse through the schema
756
- for key, value in obj.items():
757
- if key in [
758
- "properties",
759
- "items",
760
- "allOf",
761
- "oneOf",
762
- "anyOf",
763
- ]:
764
- if isinstance(value, dict):
765
- if _has_open_props(value, f"{path}.{key}"):
766
- return True
767
- elif isinstance(value, list):
768
- for i, item in enumerate(value):
769
- if _has_open_props(
770
- item,
771
- f"{path}.{key}[{i}]",
772
- ):
773
- return True
774
- elif isinstance(value, dict) and key not in [
775
- "description",
776
- "type",
777
- "enum",
778
- ]:
779
- if _has_open_props(value, f"{path}.{key}"):
780
- return True
781
- return False
749
+ parameters = schema["function"].get("parameters", {})
750
+ if not parameters:
751
+ # Empty parameters - use minimal valid schema
752
+ parameters = {
753
+ "type": "object",
754
+ "properties": {},
755
+ "additionalProperties": False,
756
+ }
757
+ schema["function"]["parameters"] = parameters
782
758
 
783
- # Check if schema has dynamic additionalProperties
784
- if _has_open_props(schema["function"].get("parameters", {})):
785
- # Can't use strict mode with dynamic additionalProperties
786
- schema["function"]["strict"] = False
787
- tool.set_openai_tool_schema(schema)
788
- logger.warning(
789
- f"Tool '{tool.get_function_name()}' has "
790
- f"dynamic additionalProperties and cannot use "
791
- f"strict mode"
792
- )
793
- return tool
759
+ # MCP spec doesn't require 'properties', but OpenAI spec does
760
+ if (
761
+ parameters.get("type") == "object"
762
+ and "properties" not in parameters
763
+ ):
764
+ parameters["properties"] = {}
794
765
 
795
- # Now check for blocking issues after sanitization
796
- issues = _has_strict_mode_issues(schema)
797
- if issues:
798
- # Can't use strict mode
766
+ try:
767
+ # _check_schema_limits(parameters)
768
+
769
+ # Check for OpenAI strict mode incompatible features
770
+ if _has_strict_mode_incompatible_features(parameters):
771
+ raise ValueError(
772
+ "Schema contains features "
773
+ "incompatible with strict mode"
774
+ )
775
+
776
+ strict_params = ensure_strict_json_schema(parameters)
777
+ schema["function"]["parameters"] = strict_params
778
+ schema["function"]["strict"] = True
779
+ except Exception as e:
780
+ # Fallback to non-strict mode on any failure
799
781
  schema["function"]["strict"] = False
800
- tool.set_openai_tool_schema(schema)
801
782
  logger.warning(
802
- f"Tool '{tool.get_function_name()}' has "
803
- f"issues preventing strict mode: "
804
- f"{'; '.join(issues[:3])}{'...' if len(issues) > 3 else ''}" # noqa: E501
783
+ f"Tool '{tool.get_function_name()}' "
784
+ f"cannot use strict mode: {e}"
805
785
  )
806
- return tool
807
-
808
- # Enable strict mode
809
- schema["function"]["strict"] = True
810
-
811
- parameters = schema["function"].get("parameters", {})
812
- if parameters:
813
- # Validate and fix the parameters schema
814
- _validate_and_fix_schema(parameters)
815
-
816
- # Check schema limits
817
- _check_schema_limits(parameters)
818
786
 
819
787
  tool.set_openai_tool_schema(schema)
820
- logger.debug(
821
- f"Updated tool '{tool.get_function_name()}' to strict mode"
822
- )
823
788
 
824
789
  except Exception as e:
825
- # If we can't make it strict, disable strict mode
790
+ # Final fallback - ensure tool still works
826
791
  try:
827
- if "function" in schema:
828
- schema["function"]["strict"] = False
829
- tool.set_openai_tool_schema(schema)
792
+ current_schema = tool.get_openai_tool_schema()
793
+ if "function" in current_schema:
794
+ current_schema["function"]["strict"] = False
795
+ tool.set_openai_tool_schema(current_schema)
830
796
  logger.warning(
831
- f"Failed to ensure strict schema for "
832
- f"tool '{tool.get_function_name()}': {str(e)[:100]}. "
833
- f"Setting strict=False."
797
+ f"Error processing schema for tool "
798
+ f"'{tool.get_function_name()}': {str(e)[:100]}. "
799
+ f"Using non-strict mode."
834
800
  )
835
801
  except Exception as inner_e:
836
- # If even setting strict=False fails, log the error
837
802
  logger.error(
838
- f"Critical error processing "
839
- f"tool '{tool.get_function_name()}': {inner_e}. "
803
+ f"Critical error processing tool "
804
+ f"'{tool.get_function_name()}': {inner_e}. "
840
805
  f"Tool may not function correctly."
841
806
  )
842
807
 
@@ -879,6 +844,7 @@ class MCPToolkit(BaseToolkit):
879
844
  )
880
845
 
881
846
  all_tools = []
847
+ seen_names: set[str] = set()
882
848
  for i, client in enumerate(self.clients):
883
849
  try:
884
850
  client_tools = client.get_tools()
@@ -887,6 +853,14 @@ class MCPToolkit(BaseToolkit):
887
853
  strict_tools = []
888
854
  for tool in client_tools:
889
855
  strict_tool = self._ensure_strict_tool_schema(tool)
856
+ name = strict_tool.get_function_name()
857
+ if name in seen_names:
858
+ logger.warning(
859
+ f"Duplicate tool name detected and "
860
+ f"skipped: '{name}' from client {i+1}"
861
+ )
862
+ continue
863
+ seen_names.add(name)
890
864
  strict_tools.append(strict_tool)
891
865
 
892
866
  all_tools.extend(strict_tools)
@@ -237,7 +237,8 @@ class SlackToolkit(BaseToolkit):
237
237
  ) -> str:
238
238
  r"""Send a message to a Slack channel. When use this function you must
239
239
  call `get_slack_channel_information` function first to get the
240
- `channel id`.
240
+ `channel id`. If use user, you must use `get_slack_user_list`
241
+ function first to get the user id.
241
242
 
242
243
  Args:
243
244
  message (str): The message to send.
@@ -306,6 +307,52 @@ class SlackToolkit(BaseToolkit):
306
307
  except SlackApiError as e:
307
308
  return f"Error deleting message: {e.response['error']}"
308
309
 
310
+ def get_slack_user_list(self) -> str:
311
+ r"""Retrieve a list of all users in the Slack workspace.
312
+
313
+ Returns:
314
+ str: A JSON string representing a list of users. Each user
315
+ object contains 'id', 'name'.
316
+ """
317
+ from slack_sdk.errors import SlackApiError
318
+
319
+ try:
320
+ slack_client = self._login_slack()
321
+ response = slack_client.users_list()
322
+ users = response["members"]
323
+ filtered_users = [
324
+ {
325
+ "id": user["id"],
326
+ "name": user["name"],
327
+ }
328
+ for user in users
329
+ ]
330
+
331
+ return json.dumps(filtered_users, ensure_ascii=False)
332
+ except SlackApiError as e:
333
+ return f"Error retrieving user list: {e.response['error']}"
334
+
335
+ def get_slack_user_info(self, user_id: str) -> str:
336
+ r"""Retrieve information about a specific user in the Slack workspace.
337
+ normally, you don't need to use this method, when you need to get a
338
+ user's detailed information, use this method. Use `get_slack_user_list`
339
+ function first to get the user id.
340
+
341
+ Args:
342
+ user_id (str): The ID of the user to retrieve information about.
343
+
344
+ Returns:
345
+ str: A JSON string representing the user's information.
346
+ """
347
+ from slack_sdk.errors import SlackApiError
348
+
349
+ try:
350
+ slack_client = self._login_slack()
351
+ response = slack_client.users_info(user=user_id)
352
+ return json.dumps(response, ensure_ascii=False)
353
+ except SlackApiError as e:
354
+ return f"Error retrieving user info: {e.response['error']}"
355
+
309
356
  def get_tools(self) -> List[FunctionTool]:
310
357
  r"""Returns a list of FunctionTool objects representing the
311
358
  functions in the toolkit.
@@ -322,4 +369,6 @@ class SlackToolkit(BaseToolkit):
322
369
  FunctionTool(self.get_slack_channel_message),
323
370
  FunctionTool(self.send_slack_message),
324
371
  FunctionTool(self.delete_slack_message),
372
+ FunctionTool(self.get_slack_user_list),
373
+ FunctionTool(self.get_slack_user_info),
325
374
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: camel-ai
3
- Version: 0.2.76a1
3
+ Version: 0.2.76a3
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
@@ -448,12 +448,52 @@ Join us ([*Discord*](https://discord.camel-ai.org/) or [*WeChat*](https://ghli.o
448
448
  </div>
449
449
 
450
450
  <div align="center">
451
- <img src="docs/images/star.gif" alt="Star" width="186" height="60">
451
+ <img src="docs/images/stars.gif" alt="Star">
452
452
  </a>
453
453
  </div>
454
454
 
455
455
  <br>
456
456
 
457
+ <details>
458
+ <summary><kbd>Table of contents</kbd></summary>
459
+
460
+ <br/>
461
+
462
+ - [CAMEL Framework Design Principles](#camel-framework-design-principles)
463
+ - [Why Use CAMEL for Your Research?](#why-use-camel-for-your-research)
464
+ - [What Can You Build With CAMEL?](#what-can-you-build-with-camel)
465
+ - [Data Generation](#1-data-generation)
466
+ - [Task Automation](#2-task-automation)
467
+ - [World Simulation](#3-world-simulation)
468
+ - [Quick Start](#quick-start)
469
+ - [Starting with ChatAgent](#starting-with-chatagent)
470
+ - [Seeking Help](#seeking-help)
471
+ - [Tech Stack](#tech-stack)
472
+ - [Research](#research)
473
+ - [Synthetic Datasets](#synthetic-datasets)
474
+ - [Cookbooks (Usecases)](#cookbooks-usecases)
475
+ - [Basic Concepts](#1-basic-concepts)
476
+ - [Advanced Features](#2-advanced-features)
477
+ - [Model Training & Data Generation](#3-model-training--data-generation)
478
+ - [Multi-Agent Systems & Applications](#4-multi-agent-systems--applications)
479
+ - [Data Processing](#5-data-processing)
480
+ - [Real-World Usecases](#real-world-usecases)
481
+ - [🧱 Built with CAMEL (Real-world Producs & Research)](#-built-with-camel-real-world-producs--research)
482
+ - [Research Projects](#research-projects)
483
+ - [Product Projects](#product-projects)
484
+ - [🗓️ Events](#️-events)
485
+ - [Contributing to CAMEL](#contributing-to-camel)
486
+ - [Community & Contact](#community--contact)
487
+ - [Citation](#citation)
488
+ - [Acknowledgment](#acknowledgment)
489
+ - [License](#license)
490
+
491
+ ####
492
+
493
+ <br/>
494
+
495
+ </details>
496
+
457
497
 
458
498
  ## CAMEL Framework Design Principles
459
499
 
@@ -687,6 +727,18 @@ We believe that studying these agents on a large scale offers valuable insights
687
727
 
688
728
  **Explore our research projects:**
689
729
 
730
+ <div align="center">
731
+ <a href="https://github.com/camel-ai/owl">
732
+ <img src="docs/images/owl.png" alt="OWL">
733
+ </a>
734
+ </div>
735
+
736
+ <div align="center">
737
+ <a href="https://oasis.camel-ai.org/">
738
+ <img src="docs/images/oasis.png" alt="OASIS">
739
+ </a>
740
+ </div>
741
+
690
742
  <div align="center">
691
743
  <a href="https://crab.camel-ai.org/">
692
744
  <img src="docs/images/crab.png" alt="CRAB">
@@ -694,14 +746,14 @@ We believe that studying these agents on a large scale offers valuable insights
694
746
  </div>
695
747
 
696
748
  <div align="center">
697
- <a href="https://agent-trust.camel-ai.org/">
698
- <img src="docs/images/agent_trust.png" alt="Agent Trust">
749
+ <a href="https://github.com/camel-ai/loong">
750
+ <img src="docs/images/loong.png" alt="Loong">
699
751
  </a>
700
752
  </div>
701
753
 
702
754
  <div align="center">
703
- <a href="https://oasis.camel-ai.org/">
704
- <img src="docs/images/oasis.png" alt="OASIS">
755
+ <a href="https://agent-trust.camel-ai.org/">
756
+ <img src="docs/images/agent_trust.png" alt="Agent Trust">
705
757
  </a>
706
758
  </div>
707
759
 
@@ -835,6 +887,31 @@ Real-world usecases demonstrating how CAMEL’s multi-agent framework enables re
835
887
 
836
888
  <br>
837
889
 
890
+ ## 🧱 Built with CAMEL (Real-world Producs & Research)
891
+ <div align="left">
892
+ <a href="https://www.camel-ai.org/">
893
+ <img src="docs/images/built_with_CAMEL.png" alt="Built with CAMEL" height="40px">
894
+ </a>
895
+ </div>
896
+
897
+ ### Research Projects
898
+
899
+ | Name | Description |
900
+ |:---|:---|
901
+ | **[ChatDev](https://github.com/OpenBMB/ChatDev/tree/main/camel)** | Communicative Agents for software Development |
902
+ | **[Paper2Poster](https://github.com/Paper2Poster/Paper2Poster)** | Multimodal poster automation from scientific papers |
903
+
904
+ ### Product Projects
905
+
906
+ | Name | Description |
907
+ |:---|:---|
908
+ | **[Eigent](https://www.eigent.ai/)** | The World First Multi-agent Workforce |
909
+ | **[EigentBot](https://bot.eigent.ai/)** | One EigentBot,
910
+ Every Code Answer |
911
+ | **[Matrix](https://matrix.eigent.ai/)** | Social Media Simulation |
912
+ | **[AI Geometric](https://www.linkedin.com/posts/aigeometric_ai-interviewpreparation-careerdevelopment-activity-7261428422516555776-MtaK/?utm_source=share&utm_medium=member_desktop&rcm=ACoAAChHluEB9xRwkjiJ6VSAzqM2Y-U4NI2sKGY)** | AI-powered interview copilot |
913
+ | **[Log10](https://github.com/log10-io/log10/blob/main/src/log10/agents/camel.py)** | AI accuracy, delivered |
914
+
838
915
 
839
916
  ## 🗓️ Events
840
917
 
@@ -1,4 +1,4 @@
1
- camel/__init__.py,sha256=6I-bm5fZpPWlOpBZ5pxXypAQXhzfcXQx0Ah3FJJSZVk,901
1
+ camel/__init__.py,sha256=9DLMrXKU5X35VRsz3WSLMoC2MnzWuYKWD0SHeoLtyIc,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=WgEwael_eT6D-lVAKHpKIpwXSTjvLbny5jbV1Ab8lnA,5760
@@ -128,7 +128,7 @@ camel/environments/models.py,sha256=jVcCyU7xObKoWPnkshmPqyyKi3AOiMVVtUZA-tWEYUU,
128
128
  camel/environments/multi_step.py,sha256=HPIH2W-iWsmtDLeN1gjxo7LoAnMQQZzdmfjhmRoBAxg,8534
129
129
  camel/environments/rlcards_env.py,sha256=OclnrJf7HgjuGUgYP6lISBmzTyG_gThHgPKwTUrG9y8,29861
130
130
  camel/environments/single_step.py,sha256=zrAXLOBGGKQFnGENasUnmFO_T-rjppGPK2CRBLGu7aQ,23019
131
- camel/environments/tic_tac_toe.py,sha256=axRDN9yUVH8PzACkD6zenWiQKFDaupGsQ7NmSvbJCIc,19080
131
+ camel/environments/tic_tac_toe.py,sha256=grw4z4GJBVS--IvMHUCy2IJy40z4fz6cG2HC0UmEktw,19110
132
132
  camel/extractors/__init__.py,sha256=lgtDl8zWvN826fJVKqRv05w556YZ-EdrHwdzKphywgA,1097
133
133
  camel/extractors/base.py,sha256=3jvuZpq27nlADDCX3GfubOpeb_zt-E9rzxF3x4lYm8s,10404
134
134
  camel/extractors/python_strategies.py,sha256=zHAkshnO9o-uvLtCuVOCKoA2PzetBTnkNx1Qy_3j_pE,8113
@@ -183,7 +183,7 @@ camel/models/anthropic_model.py,sha256=GlerIhIc7uGhzIsQoZw-_8CGOdcZT8DC_95V3hx3q
183
183
  camel/models/aws_bedrock_model.py,sha256=0JdsLxfi-coI8LtSPNewsaeR43CwC0qG2Gm_iY-ZCdo,4073
184
184
  camel/models/azure_openai_model.py,sha256=MSjb5E3b6BHQg896P-Mj00Q0xPq0ibi0tO2T87Fj25s,17802
185
185
  camel/models/base_audio_model.py,sha256=_VUWh1L3rh8mldNvM5R6jBOKtvmTeBKJyRxAdPJmPlY,3324
186
- camel/models/base_model.py,sha256=82MVxlWQXEN_gmVTgbAr0vP02opn0bn8pDK6K-3OtQE,19128
186
+ camel/models/base_model.py,sha256=aty9oIZowjt5Mxi0aB7ifHUmQO-78UMfUFLPNm10lGg,20164
187
187
  camel/models/cohere_model.py,sha256=9H2F8bjwxPgwSwgPPRoOy090dQKBboQxnlk-94FoDIk,16719
188
188
  camel/models/crynux_model.py,sha256=rslcj7FbbeKZzYS2n23fEe5Ua77FY1gXmUAy6TBY2Yw,3545
189
189
  camel/models/deepseek_model.py,sha256=qk5RrzYddKn3usddsB9NsRG5UfNR-dh2Ymwqw-NsYNs,10432
@@ -285,7 +285,7 @@ camel/societies/workforce/structured_output_handler.py,sha256=xr8szFN86hg3jQ825a
285
285
  camel/societies/workforce/task_channel.py,sha256=TXRwiqtmRPdelEmFCVN3jhd5XpgaSLwy9uHPtGecujA,11418
286
286
  camel/societies/workforce/utils.py,sha256=THgNHSeZsNVnjTzQTur3qCJhi72MrDS8X2gPET174cI,8434
287
287
  camel/societies/workforce/worker.py,sha256=MtUqYkTC9V-PIIRwSkKiB9w_YSu92iOpoha2rktEiQ0,6248
288
- camel/societies/workforce/workforce.py,sha256=UsU46YWWFnJV_YbYQwwv6COZ3iPJnLY3XetDhdI9w_o,142328
288
+ camel/societies/workforce/workforce.py,sha256=h7Df4zoo5UuB-GpYTHotdcIwFDS6ESANLQLWzo56urE,142583
289
289
  camel/societies/workforce/workforce_logger.py,sha256=0YT__ys48Bgn0IICKIZBmSWhON-eA1KShebjCdn5ppE,24525
290
290
  camel/storages/__init__.py,sha256=RwpEyvxpMbJzVDZJJygeBg4AzyYMkTjjkfB53hTuqGo,2141
291
291
  camel/storages/graph_storages/__init__.py,sha256=G29BNn651C0WTOpjCl4QnVM-4B9tcNh8DdmsCiONH8Y,948
@@ -354,7 +354,7 @@ camel/toolkits/klavis_toolkit.py,sha256=ZKerhgz5e-AV-iv0ftf07HgWikknIHjB3EOQswfu
354
354
  camel/toolkits/linkedin_toolkit.py,sha256=wn4eXwYYlVA7doTna7k7WYhUqTBF83W79S-UJs_IQr0,8065
355
355
  camel/toolkits/markitdown_toolkit.py,sha256=lwN6qQY8TLZkNWOqzeKZG3Fku-HMpGFrdRwhtPaJSlw,3844
356
356
  camel/toolkits/math_toolkit.py,sha256=SJbzT6akHRlmqo1QwCj1f7-6pEv0sNKJbcYvYAylHQw,5439
357
- camel/toolkits/mcp_toolkit.py,sha256=FbBKyz7f6y2hA-lBVsh3K-_gyz2rVDwe3uvaK8OsuRc,41442
357
+ camel/toolkits/mcp_toolkit.py,sha256=Y6bwq_Wy1FeBi7uVjiR6SrN1GQeJJDqgDt6Hcl26gOw,37530
358
358
  camel/toolkits/memory_toolkit.py,sha256=TeKYd5UMwgjVpuS2orb-ocFL13eUNKujvrFOruDCpm8,4436
359
359
  camel/toolkits/meshy_toolkit.py,sha256=NbgdOBD3FYLtZf-AfonIv6-Q8-8DW129jsaP1PqI2rs,7126
360
360
  camel/toolkits/message_agent_toolkit.py,sha256=yWvAaxoxAvDEtD7NH7IkkHIyfWIYK47WZhn5E_RaxKo,22661
@@ -380,7 +380,7 @@ camel/toolkits/screenshot_toolkit.py,sha256=IwfvfLSfqrEywvPlDbtYJe1qcbrO5uC3Mxxv
380
380
  camel/toolkits/search_toolkit.py,sha256=0SA2FLefCFmW5B4zMpllr4V-kJT2l9_KhwFuVRH2Co0,56052
381
381
  camel/toolkits/searxng_toolkit.py,sha256=a2GtE4FGSrmaIVvX6Yide-abBYD1wsHqitnDlx9fdVg,7664
382
382
  camel/toolkits/semantic_scholar_toolkit.py,sha256=Rh7eA_YPxV5pvPIzhjjvpr3vtlaCniJicrqzkPWW9_I,11634
383
- camel/toolkits/slack_toolkit.py,sha256=iBVkDIpfsR5QwaNCqA4O4UkCwpdLhJjYCA8Gka7U9e8,12050
383
+ camel/toolkits/slack_toolkit.py,sha256=hlU7EmXTIrtvG5Mr35jfbQBCJW_eyOSS1FSncXYfj9U,13935
384
384
  camel/toolkits/stripe_toolkit.py,sha256=07swo5znGTnorafC1uYLKB4NRcJIOPOx19J7tkpLYWk,10102
385
385
  camel/toolkits/sympy_toolkit.py,sha256=BAQnI8EFJydNUpKQWXBdleQ1Cm-srDBhFlqp9V9pbPQ,33757
386
386
  camel/toolkits/task_planning_toolkit.py,sha256=Ttw9fHae4omGC1SA-6uaeXVHJ1YkwiVloz_hO-fm1gw,4855
@@ -398,9 +398,9 @@ camel/toolkits/wolfram_alpha_toolkit.py,sha256=qeIM8ySn5ilcExBWtx-hDOc35bNcebLVn
398
398
  camel/toolkits/zapier_toolkit.py,sha256=A83y1UcfuopH7Fx82pORzypl1StbhBjB2HhyOqYa300,7124
399
399
  camel/toolkits/hybrid_browser_toolkit/__init__.py,sha256=vxjWhq7GjUKE5I9RGQU_GoikZJ-AVK4ertdvEqp9pd0,802
400
400
  camel/toolkits/hybrid_browser_toolkit/config_loader.py,sha256=1FVZzDucDsC9IB6EcfP9TMgD3Z60vOniY4dk5TB1dsg,7595
401
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py,sha256=lKZqDiRPVi07flrACCjFtANDsp-n6kstE1iF7OOhDa4,8930
401
+ camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py,sha256=56o6ht3SwvkUbULFfDOQMx3eE-WVp8L81zZ_hp4tjfM,12195
402
402
  camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py,sha256=KGvtdJdUJ86VacPmL4BYd0EZjscH6Z2wXtraXAQud1Y,56214
403
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py,sha256=Ts30Ue9MEcwJU4C0KSYANvEw_YZmjeGe3quzZJ8O5Ek,37160
403
+ camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py,sha256=Z0LGrghQ9iLXJ2DKspj2_U9Qr8359lk73YH40qjkX0o,38019
404
404
  camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json,sha256=_-YE9S_C1XT59A6upQp9lLuZcC67cV9QlbwAsEKkfyw,156337
405
405
  camel/toolkits/hybrid_browser_toolkit/ts/package.json,sha256=pUQm0xwXR7ZyWNv6O2QtHW00agnfAoX9F_XGXZlAxl4,745
406
406
  camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json,sha256=SwpQnq4Q-rwRobF2iWrP96mgmgwaVPZEv-nii5QIYEU,523
@@ -479,7 +479,7 @@ camel/verifiers/math_verifier.py,sha256=tA1D4S0sm8nsWISevxSN0hvSVtIUpqmJhzqfbuMo
479
479
  camel/verifiers/models.py,sha256=GdxYPr7UxNrR1577yW4kyroRcLGfd-H1GXgv8potDWU,2471
480
480
  camel/verifiers/physics_verifier.py,sha256=c1grrRddcrVN7szkxhv2QirwY9viIRSITWeWFF5HmLs,30187
481
481
  camel/verifiers/python_verifier.py,sha256=ogTz77wODfEcDN4tMVtiSkRQyoiZbHPY2fKybn59lHw,20558
482
- camel_ai-0.2.76a1.dist-info/METADATA,sha256=_UR_r1rpykgqO_LbYlG7VBIkWS_uGEJqqRJYUQ0e5T4,52058
483
- camel_ai-0.2.76a1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
484
- camel_ai-0.2.76a1.dist-info/licenses/LICENSE,sha256=id0nB2my5kG0xXeimIu5zZrbHLS6EQvxvkKkzIHaT2k,11343
485
- camel_ai-0.2.76a1.dist-info/RECORD,,
482
+ camel_ai-0.2.76a3.dist-info/METADATA,sha256=rib4FdlfEFWDE_QAGVNdTIJg4SxJH03pxIhP0GqgNvU,54897
483
+ camel_ai-0.2.76a3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
484
+ camel_ai-0.2.76a3.dist-info/licenses/LICENSE,sha256=id0nB2my5kG0xXeimIu5zZrbHLS6EQvxvkKkzIHaT2k,11343
485
+ camel_ai-0.2.76a3.dist-info/RECORD,,