camel-ai 0.2.75a6__py3-none-any.whl → 0.2.76a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

Files changed (52) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +159 -38
  3. camel/configs/__init__.py +3 -0
  4. camel/configs/amd_config.py +70 -0
  5. camel/interpreters/__init__.py +2 -0
  6. camel/interpreters/microsandbox_interpreter.py +395 -0
  7. camel/memories/__init__.py +2 -1
  8. camel/memories/agent_memories.py +3 -1
  9. camel/memories/blocks/chat_history_block.py +17 -2
  10. camel/models/__init__.py +2 -0
  11. camel/models/amd_model.py +101 -0
  12. camel/models/model_factory.py +2 -0
  13. camel/models/openai_model.py +0 -6
  14. camel/runtimes/daytona_runtime.py +11 -12
  15. camel/societies/workforce/single_agent_worker.py +44 -38
  16. camel/storages/object_storages/google_cloud.py +1 -1
  17. camel/toolkits/__init__.py +14 -5
  18. camel/toolkits/aci_toolkit.py +45 -0
  19. camel/toolkits/code_execution.py +28 -1
  20. camel/toolkits/context_summarizer_toolkit.py +683 -0
  21. camel/toolkits/{file_write_toolkit.py → file_toolkit.py} +194 -34
  22. camel/toolkits/function_tool.py +6 -1
  23. camel/toolkits/hybrid_browser_toolkit/config_loader.py +12 -0
  24. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +19 -2
  25. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +95 -59
  26. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +619 -95
  27. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +7 -2
  28. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +115 -219
  29. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  30. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  31. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  32. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +1 -0
  33. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +39 -6
  34. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +401 -80
  35. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +9 -5
  36. camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +98 -31
  37. camel/toolkits/markitdown_toolkit.py +27 -1
  38. camel/toolkits/mcp_toolkit.py +39 -14
  39. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  40. camel/toolkits/note_taking_toolkit.py +18 -8
  41. camel/toolkits/terminal_toolkit.py +12 -2
  42. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  43. camel/toolkits/video_analysis_toolkit.py +16 -10
  44. camel/toolkits/wechat_official_toolkit.py +483 -0
  45. camel/types/enums.py +11 -0
  46. camel/utils/commons.py +2 -0
  47. camel/utils/context_utils.py +395 -0
  48. camel/utils/mcp.py +136 -2
  49. {camel_ai-0.2.75a6.dist-info → camel_ai-0.2.76a1.dist-info}/METADATA +6 -3
  50. {camel_ai-0.2.75a6.dist-info → camel_ai-0.2.76a1.dist-info}/RECORD +52 -41
  51. {camel_ai-0.2.75a6.dist-info → camel_ai-0.2.76a1.dist-info}/WHEEL +0 -0
  52. {camel_ai-0.2.75a6.dist-info → camel_ai-0.2.76a1.dist-info}/licenses/LICENSE +0 -0
@@ -35,8 +35,8 @@ from camel.tasks.task import Task, TaskState, is_task_result_insufficient
35
35
  class AgentPool:
36
36
  r"""A pool of agent instances for efficient reuse.
37
37
 
38
- This pool manages a collection of pre-cloned agents. It supports
39
- auto-scaling based ondemand and intelligent reuse of existing agents.
38
+ This pool manages a collection of pre-cloned agents with automatic
39
+ scaling and idle timeout cleanup.
40
40
 
41
41
  Args:
42
42
  base_agent (ChatAgent): The base agent to clone from.
@@ -48,6 +48,8 @@ class AgentPool:
48
48
  (default: :obj:`True`)
49
49
  idle_timeout (float): Time in seconds after which idle agents are
50
50
  removed. (default: :obj:`180.0`)
51
+ cleanup_interval (float): Fixed interval in seconds between cleanup
52
+ checks. (default: :obj:`60.0`)
51
53
  """
52
54
 
53
55
  def __init__(
@@ -56,12 +58,14 @@ class AgentPool:
56
58
  initial_size: int = 1,
57
59
  max_size: int = 10,
58
60
  auto_scale: bool = True,
59
- idle_timeout: float = 180.0, # 3 minutes
61
+ idle_timeout: float = 180.0,
62
+ cleanup_interval: float = 60.0,
60
63
  ):
61
64
  self.base_agent = base_agent
62
65
  self.max_size = max_size
63
66
  self.auto_scale = auto_scale
64
67
  self.idle_timeout = idle_timeout
68
+ self.cleanup_interval = cleanup_interval
65
69
 
66
70
  # Pool management
67
71
  self._available_agents: deque = deque()
@@ -73,6 +77,7 @@ class AgentPool:
73
77
  self._total_borrows = 0
74
78
  self._total_clones_created = 0
75
79
  self._pool_hits = 0
80
+ self._agents_cleaned = 0
76
81
 
77
82
  # Initialize pool
78
83
  self._initialize_pool(initial_size)
@@ -82,6 +87,7 @@ class AgentPool:
82
87
  for _ in range(min(size, self.max_size)):
83
88
  agent = self._create_fresh_agent()
84
89
  self._available_agents.append(agent)
90
+ self._agent_last_used[id(agent)] = time.time()
85
91
 
86
92
  def _create_fresh_agent(self) -> ChatAgent:
87
93
  r"""Create a fresh agent instance."""
@@ -94,53 +100,46 @@ class AgentPool:
94
100
  async with self._lock:
95
101
  self._total_borrows += 1
96
102
 
97
- # Try to get from available agents first
98
103
  if self._available_agents:
99
104
  agent = self._available_agents.popleft()
100
105
  self._in_use_agents.add(id(agent))
101
106
  self._pool_hits += 1
102
-
103
- # Reset the agent state
104
- agent.reset()
105
107
  return agent
106
108
 
107
- # Check if we can create new agents
108
- total_agents = len(self._available_agents) + len(
109
- self._in_use_agents
110
- )
111
- if total_agents < self.max_size:
109
+ # Check if we can create a new agent
110
+ if len(self._in_use_agents) < self.max_size or self.auto_scale:
112
111
  agent = self._create_fresh_agent()
113
112
  self._in_use_agents.add(id(agent))
114
113
  return agent
115
114
 
116
- # Pool exhausted, wait and retry or create temporary agent
117
- if self.auto_scale:
118
- # Create a temporary agent that won't be returned to pool
119
- return self._create_fresh_agent()
120
- else:
121
- # Wait for an agent to become available
122
- while not self._available_agents:
123
- await asyncio.sleep(0.1)
124
-
125
- agent = self._available_agents.popleft()
126
- self._in_use_agents.add(id(agent))
127
- agent.reset()
128
- return agent
115
+ # Wait for available agent
116
+ while True:
117
+ async with self._lock:
118
+ if self._available_agents:
119
+ agent = self._available_agents.popleft()
120
+ self._in_use_agents.add(id(agent))
121
+ self._pool_hits += 1
122
+ return agent
123
+ await asyncio.sleep(0.05)
129
124
 
130
125
  async def return_agent(self, agent: ChatAgent) -> None:
131
126
  r"""Return an agent to the pool."""
127
+ agent_id = id(agent)
128
+
132
129
  async with self._lock:
133
- agent_id = id(agent)
130
+ if agent_id not in self._in_use_agents:
131
+ return
134
132
 
135
- if agent_id in self._in_use_agents:
136
- self._in_use_agents.remove(agent_id)
133
+ self._in_use_agents.discard(agent_id)
137
134
 
138
- # Only return to pool if we're under max size
139
- if len(self._available_agents) < self.max_size:
140
- # Reset agent state before returning to pool
141
- agent.reset()
142
- self._available_agents.append(agent)
143
- self._agent_last_used[agent_id] = time.time()
135
+ # Only add back to pool if under max size
136
+ if len(self._available_agents) < self.max_size:
137
+ agent.reset()
138
+ self._agent_last_used[agent_id] = time.time()
139
+ self._available_agents.append(agent)
140
+ else:
141
+ # Remove tracking for agents not returned to pool
142
+ self._agent_last_used.pop(agent_id, None)
144
143
 
145
144
  async def cleanup_idle_agents(self) -> None:
146
145
  r"""Remove idle agents from the pool to free memory."""
@@ -148,30 +147,35 @@ class AgentPool:
148
147
  return
149
148
 
150
149
  async with self._lock:
150
+ if not self._available_agents:
151
+ return
152
+
151
153
  current_time = time.time()
152
154
  agents_to_remove = []
153
155
 
154
156
  for agent in list(self._available_agents):
155
157
  agent_id = id(agent)
156
158
  last_used = self._agent_last_used.get(agent_id, current_time)
157
-
158
159
  if current_time - last_used > self.idle_timeout:
159
160
  agents_to_remove.append(agent)
160
161
 
161
162
  for agent in agents_to_remove:
162
163
  self._available_agents.remove(agent)
163
- agent_id = id(agent)
164
- self._agent_last_used.pop(agent_id, None)
164
+ self._agent_last_used.pop(id(agent), None)
165
+ self._agents_cleaned += 1
165
166
 
166
167
  def get_stats(self) -> dict:
167
168
  r"""Get pool statistics."""
168
169
  return {
169
170
  "available_agents": len(self._available_agents),
170
171
  "in_use_agents": len(self._in_use_agents),
172
+ "pool_size": len(self._available_agents)
173
+ + len(self._in_use_agents),
171
174
  "total_borrows": self._total_borrows,
172
175
  "total_clones_created": self._total_clones_created,
173
176
  "pool_hits": self._pool_hits,
174
177
  "hit_rate": self._pool_hits / max(self._total_borrows, 1),
178
+ "agents_cleaned_up": self._agents_cleaned,
175
179
  }
176
180
 
177
181
 
@@ -477,7 +481,9 @@ class SingleAgentWorker(Worker):
477
481
  r"""Periodically clean up idle agents from the pool."""
478
482
  while True:
479
483
  try:
480
- await asyncio.sleep(60) # Cleanup every minute
484
+ # Fixed interval cleanup
485
+ await asyncio.sleep(self.agent_pool.cleanup_interval)
486
+
481
487
  if self.agent_pool:
482
488
  await self.agent_pool.cleanup_idle_agents()
483
489
  except asyncio.CancelledError:
@@ -46,7 +46,7 @@ class GoogleCloudStorage(BaseObjectStorage):
46
46
  create_if_not_exists: bool = True,
47
47
  anonymous: bool = False,
48
48
  ) -> None:
49
- from google.cloud import storage
49
+ from google.cloud import storage # type: ignore[attr-defined]
50
50
 
51
51
  self.create_if_not_exists = create_if_not_exists
52
52
 
@@ -23,7 +23,7 @@ from .open_api_specs.security_config import openapi_security_config
23
23
  from .math_toolkit import MathToolkit
24
24
  from .search_toolkit import SearchToolkit
25
25
  from .weather_toolkit import WeatherToolkit
26
- from .openai_image_toolkit import OpenAIImageToolkit
26
+ from .image_generation_toolkit import ImageGenToolkit, OpenAIImageToolkit
27
27
  from .ask_news_toolkit import AskNewsToolkit, AsyncAskNewsToolkit
28
28
  from .linkedin_toolkit import LinkedInToolkit
29
29
  from .reddit_toolkit import RedditToolkit
@@ -40,6 +40,7 @@ from .google_calendar_toolkit import GoogleCalendarToolkit
40
40
  from .arxiv_toolkit import ArxivToolkit
41
41
  from .slack_toolkit import SlackToolkit
42
42
  from .whatsapp_toolkit import WhatsAppToolkit
43
+ from .wechat_official_toolkit import WeChatOfficialToolkit
43
44
  from .twitter_toolkit import TwitterToolkit
44
45
  from .open_api_toolkit import OpenAPIToolkit
45
46
  from .retrieval_toolkit import RetrievalToolkit
@@ -61,7 +62,7 @@ from .image_analysis_toolkit import ImageAnalysisToolkit
61
62
  from .mcp_toolkit import MCPToolkit
62
63
  from .browser_toolkit import BrowserToolkit
63
64
  from .async_browser_toolkit import AsyncBrowserToolkit
64
- from .file_write_toolkit import FileWriteToolkit
65
+ from .file_toolkit import FileToolkit, FileWriteToolkit
65
66
  from .pptx_toolkit import PPTXToolkit
66
67
  from .terminal_toolkit import TerminalToolkit
67
68
  from .pubmed_toolkit import PubMedToolkit
@@ -87,7 +88,10 @@ from .message_agent_toolkit import AgentCommunicationToolkit
87
88
  from .web_deploy_toolkit import WebDeployToolkit
88
89
  from .screenshot_toolkit import ScreenshotToolkit
89
90
  from .message_integration import ToolkitMessageIntegration
91
+ from .context_summarizer_toolkit import ContextSummarizerToolkit
90
92
  from .notion_mcp_toolkit import NotionMCPToolkit
93
+ from .vertex_ai_veo_toolkit import VertexAIVeoToolkit
94
+ from .minimax_mcp_toolkit import MinimaxMCPToolkit
91
95
 
92
96
  __all__ = [
93
97
  'BaseToolkit',
@@ -102,7 +106,8 @@ __all__ = [
102
106
  'SearchToolkit',
103
107
  'SlackToolkit',
104
108
  'WhatsAppToolkit',
105
- 'OpenAIImageToolkit',
109
+ 'WeChatOfficialToolkit',
110
+ 'ImageGenToolkit',
106
111
  'TwitterToolkit',
107
112
  'WeatherToolkit',
108
113
  'RetrievalToolkit',
@@ -135,7 +140,8 @@ __all__ = [
135
140
  'ImageAnalysisToolkit',
136
141
  'BrowserToolkit',
137
142
  'AsyncBrowserToolkit',
138
- 'FileWriteToolkit',
143
+ 'FileToolkit',
144
+ 'FileWriteToolkit', # Deprecated, use FileToolkit instead
139
145
  'PPTXToolkit',
140
146
  'TerminalToolkit',
141
147
  'PubMedToolkit',
@@ -151,7 +157,7 @@ __all__ = [
151
157
  'PlaywrightMCPToolkit',
152
158
  'WolframAlphaToolkit',
153
159
  'BohriumToolkit',
154
- 'OpenAIImageToolkit',
160
+ 'OpenAIImageToolkit', # Backward compatibility
155
161
  'TaskPlanningToolkit',
156
162
  'HybridBrowserToolkit',
157
163
  'EdgeOnePagesMCPToolkit',
@@ -164,5 +170,8 @@ __all__ = [
164
170
  'ScreenshotToolkit',
165
171
  'RegisteredAgentToolkit',
166
172
  'ToolkitMessageIntegration',
173
+ 'ContextSummarizerToolkit',
167
174
  'NotionMCPToolkit',
175
+ 'VertexAIVeoToolkit',
176
+ 'MinimaxMCPToolkit',
168
177
  ]
@@ -12,6 +12,7 @@
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
 
15
+ import asyncio
15
16
  import os
16
17
  from typing import TYPE_CHECKING, Dict, List, Optional, Union
17
18
 
@@ -408,6 +409,38 @@ class ACIToolkit(BaseToolkit):
408
409
  )
409
410
  return result
410
411
 
412
+ async def aexecute_function(
413
+ self,
414
+ function_name: str,
415
+ function_arguments: Dict,
416
+ linked_account_owner_id: str,
417
+ allowed_apps_only: bool = False,
418
+ ) -> Dict:
419
+ r"""Execute a function call asynchronously.
420
+
421
+ Args:
422
+ function_name (str): Name of the function to execute.
423
+ function_arguments (Dict): Arguments to pass to the function.
424
+ linked_account_owner_id (str): To specify the end-user (account
425
+ owner) on behalf of whom you want to execute functions
426
+ You need to first link corresponding account with the same
427
+ owner id in the ACI dashboard (https://platform.aci.dev).
428
+ allowed_apps_only (bool): If true, only returns functions/apps
429
+ that are allowed to be used by the agent/accessor, identified
430
+ by the api key. (default: :obj:`False`)
431
+
432
+ Returns:
433
+ Dict: Result of the function execution
434
+ """
435
+ result = await asyncio.to_thread(
436
+ self.client.handle_function_call,
437
+ function_name,
438
+ function_arguments,
439
+ linked_account_owner_id,
440
+ allowed_apps_only,
441
+ )
442
+ return result
443
+
411
444
  def get_tools(self) -> List[FunctionTool]:
412
445
  r"""Get a list of tools (functions) available in the configured apps.
413
446
 
@@ -434,6 +467,8 @@ class ACIToolkit(BaseToolkit):
434
467
  FunctionTool(self.delete_linked_account),
435
468
  FunctionTool(self.function_definition),
436
469
  FunctionTool(self.search_function),
470
+ FunctionTool(self.execute_function),
471
+ FunctionTool(self.aexecute_function),
437
472
  ]
438
473
 
439
474
  for function in _all_function:
@@ -448,6 +483,16 @@ class ACIToolkit(BaseToolkit):
448
483
  linked_account_owner_id=self.linked_account_owner_id,
449
484
  )
450
485
 
486
+ async def async_dummy_func(*, schema=schema, **kwargs):
487
+ return await self.aexecute_function(
488
+ function_name=schema['function']['name'],
489
+ function_arguments=kwargs,
490
+ linked_account_owner_id=self.linked_account_owner_id,
491
+ )
492
+
493
+ # Add async_call method to the sync function for compatibility
494
+ dummy_func.async_call = async_dummy_func # type: ignore[attr-defined]
495
+
451
496
  tool = FunctionTool(
452
497
  func=dummy_func,
453
498
  openai_tool_schema=schema,
@@ -18,6 +18,7 @@ from camel.interpreters import (
18
18
  E2BInterpreter,
19
19
  InternalPythonInterpreter,
20
20
  JupyterKernelInterpreter,
21
+ MicrosandboxInterpreter,
21
22
  SubprocessInterpreter,
22
23
  )
23
24
  from camel.logger import get_logger
@@ -43,18 +44,31 @@ class CodeExecutionToolkit(BaseToolkit):
43
44
  (default: :obj:`None`)
44
45
  require_confirm (bool): Whether to require confirmation before
45
46
  executing code. (default: :obj:`False`)
47
+ timeout (Optional[float]): General timeout for toolkit operations.
48
+ (default: :obj:`None`)
49
+ microsandbox_config (Optional[dict]): Configuration for microsandbox
50
+ interpreter. Available keys: 'server_url', 'api_key',
51
+ 'namespace', 'sandbox_name', 'timeout'.
52
+ If None, uses default configuration. (default: :obj:`None`)
46
53
  """
47
54
 
48
55
  def __init__(
49
56
  self,
50
57
  sandbox: Literal[
51
- "internal_python", "jupyter", "docker", "subprocess", "e2b"
58
+ "internal_python",
59
+ "jupyter",
60
+ "docker",
61
+ "subprocess",
62
+ "e2b",
63
+ "microsandbox",
52
64
  ] = "subprocess",
53
65
  verbose: bool = False,
54
66
  unsafe_mode: bool = False,
55
67
  import_white_list: Optional[List[str]] = None,
56
68
  require_confirm: bool = False,
57
69
  timeout: Optional[float] = None,
70
+ # Microsandbox configuration dictionary
71
+ microsandbox_config: Optional[dict] = None,
58
72
  ) -> None:
59
73
  super().__init__(timeout=timeout)
60
74
  self.verbose = verbose
@@ -68,6 +82,7 @@ class CodeExecutionToolkit(BaseToolkit):
68
82
  DockerInterpreter,
69
83
  SubprocessInterpreter,
70
84
  E2BInterpreter,
85
+ MicrosandboxInterpreter,
71
86
  ]
72
87
 
73
88
  if sandbox == "internal_python":
@@ -95,6 +110,18 @@ class CodeExecutionToolkit(BaseToolkit):
95
110
  )
96
111
  elif sandbox == "e2b":
97
112
  self.interpreter = E2BInterpreter(require_confirm=require_confirm)
113
+ elif sandbox == "microsandbox":
114
+ # Extract parameters with proper types for microsandbox
115
+ config = microsandbox_config or {}
116
+
117
+ self.interpreter = MicrosandboxInterpreter(
118
+ require_confirm=require_confirm,
119
+ server_url=config.get("server_url"),
120
+ api_key=config.get("api_key"),
121
+ namespace=config.get("namespace", "default"),
122
+ sandbox_name=config.get("sandbox_name"),
123
+ timeout=config.get("timeout", 30),
124
+ )
98
125
  else:
99
126
  raise RuntimeError(
100
127
  f"The sandbox type `{sandbox}` is not supported."