camel-ai 0.2.70__py3-none-any.whl → 0.2.71a2__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 +1 -1
- camel/agents/chat_agent.py +61 -3
- camel/messages/func_message.py +32 -5
- camel/societies/workforce/role_playing_worker.py +4 -4
- camel/societies/workforce/single_agent_worker.py +5 -9
- camel/societies/workforce/workforce.py +304 -49
- camel/societies/workforce/workforce_logger.py +0 -1
- camel/tasks/task.py +83 -7
- camel/toolkits/craw4ai_toolkit.py +27 -7
- camel/toolkits/file_write_toolkit.py +110 -31
- camel/toolkits/human_toolkit.py +29 -9
- camel/toolkits/jina_reranker_toolkit.py +3 -4
- camel/toolkits/non_visual_browser_toolkit/browser_non_visual_toolkit.py +23 -2
- camel/toolkits/non_visual_browser_toolkit/nv_browser_session.py +53 -11
- camel/toolkits/non_visual_browser_toolkit/snapshot.js +211 -131
- camel/toolkits/non_visual_browser_toolkit/snapshot.py +9 -8
- camel/toolkits/terminal_toolkit.py +206 -64
- camel/toolkits/video_download_toolkit.py +6 -3
- camel/utils/message_summarizer.py +148 -0
- {camel_ai-0.2.70.dist-info → camel_ai-0.2.71a2.dist-info}/METADATA +4 -4
- {camel_ai-0.2.70.dist-info → camel_ai-0.2.71a2.dist-info}/RECORD +23 -22
- {camel_ai-0.2.70.dist-info → camel_ai-0.2.71a2.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.70.dist-info → camel_ai-0.2.71a2.dist-info}/licenses/LICENSE +0 -0
camel/__init__.py
CHANGED
camel/agents/chat_agent.py
CHANGED
|
@@ -13,10 +13,12 @@
|
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
+
import asyncio
|
|
16
17
|
import json
|
|
17
18
|
import logging
|
|
18
19
|
import textwrap
|
|
19
20
|
import threading
|
|
21
|
+
import time
|
|
20
22
|
import uuid
|
|
21
23
|
from collections import defaultdict
|
|
22
24
|
from pathlib import Path
|
|
@@ -173,6 +175,11 @@ class ChatAgent(BaseAgent):
|
|
|
173
175
|
stop_event (Optional[threading.Event], optional): Event to signal
|
|
174
176
|
termination of the agent's operation. When set, the agent will
|
|
175
177
|
terminate its execution. (default: :obj:`None`)
|
|
178
|
+
mask_tool_output (Optional[bool]): Whether to return a sanitized
|
|
179
|
+
placeholder instead of the raw tool output. (default: :obj:`False`)
|
|
180
|
+
pause_event (Optional[asyncio.Event]): Event to signal pause of the
|
|
181
|
+
agent's operation. When clear, the agent will pause its execution.
|
|
182
|
+
(default: :obj:`None`)
|
|
176
183
|
"""
|
|
177
184
|
|
|
178
185
|
def __init__(
|
|
@@ -206,6 +213,8 @@ class ChatAgent(BaseAgent):
|
|
|
206
213
|
max_iteration: Optional[int] = None,
|
|
207
214
|
agent_id: Optional[str] = None,
|
|
208
215
|
stop_event: Optional[threading.Event] = None,
|
|
216
|
+
mask_tool_output: bool = False,
|
|
217
|
+
pause_event: Optional[asyncio.Event] = None,
|
|
209
218
|
) -> None:
|
|
210
219
|
if isinstance(model, ModelManager):
|
|
211
220
|
self.model_backend = model
|
|
@@ -280,6 +289,9 @@ class ChatAgent(BaseAgent):
|
|
|
280
289
|
self.response_terminators = response_terminators or []
|
|
281
290
|
self.max_iteration = max_iteration
|
|
282
291
|
self.stop_event = stop_event
|
|
292
|
+
self.mask_tool_output = mask_tool_output
|
|
293
|
+
self._secure_result_store: Dict[str, Any] = {}
|
|
294
|
+
self.pause_event = pause_event
|
|
283
295
|
|
|
284
296
|
def reset(self):
|
|
285
297
|
r"""Resets the :obj:`ChatAgent` to its initial state."""
|
|
@@ -1143,6 +1155,10 @@ class ChatAgent(BaseAgent):
|
|
|
1143
1155
|
iteration_count = 0
|
|
1144
1156
|
|
|
1145
1157
|
while True:
|
|
1158
|
+
if self.pause_event is not None and not self.pause_event.is_set():
|
|
1159
|
+
while not self.pause_event.is_set():
|
|
1160
|
+
time.sleep(0.001)
|
|
1161
|
+
|
|
1146
1162
|
try:
|
|
1147
1163
|
openai_messages, num_tokens = self.memory.get_context()
|
|
1148
1164
|
accumulated_context_tokens += num_tokens
|
|
@@ -1184,6 +1200,12 @@ class ChatAgent(BaseAgent):
|
|
|
1184
1200
|
external_tool_call_requests = []
|
|
1185
1201
|
external_tool_call_requests.append(tool_call_request)
|
|
1186
1202
|
else:
|
|
1203
|
+
if (
|
|
1204
|
+
self.pause_event is not None
|
|
1205
|
+
and not self.pause_event.is_set()
|
|
1206
|
+
):
|
|
1207
|
+
while not self.pause_event.is_set():
|
|
1208
|
+
time.sleep(0.001)
|
|
1187
1209
|
tool_call_records.append(
|
|
1188
1210
|
self._execute_tool(tool_call_request)
|
|
1189
1211
|
)
|
|
@@ -1287,6 +1309,8 @@ class ChatAgent(BaseAgent):
|
|
|
1287
1309
|
step_token_usage = self._create_token_usage_tracker()
|
|
1288
1310
|
iteration_count = 0
|
|
1289
1311
|
while True:
|
|
1312
|
+
if self.pause_event is not None and not self.pause_event.is_set():
|
|
1313
|
+
await self.pause_event.wait()
|
|
1290
1314
|
try:
|
|
1291
1315
|
openai_messages, num_tokens = self.memory.get_context()
|
|
1292
1316
|
accumulated_context_tokens += num_tokens
|
|
@@ -1328,6 +1352,11 @@ class ChatAgent(BaseAgent):
|
|
|
1328
1352
|
external_tool_call_requests = []
|
|
1329
1353
|
external_tool_call_requests.append(tool_call_request)
|
|
1330
1354
|
else:
|
|
1355
|
+
if (
|
|
1356
|
+
self.pause_event is not None
|
|
1357
|
+
and not self.pause_event.is_set()
|
|
1358
|
+
):
|
|
1359
|
+
await self.pause_event.wait()
|
|
1331
1360
|
tool_call_record = await self._aexecute_tool(
|
|
1332
1361
|
tool_call_request
|
|
1333
1362
|
)
|
|
@@ -1958,14 +1987,27 @@ class ChatAgent(BaseAgent):
|
|
|
1958
1987
|
tool_call_id = tool_call_request.tool_call_id
|
|
1959
1988
|
tool = self._internal_tools[func_name]
|
|
1960
1989
|
try:
|
|
1961
|
-
|
|
1990
|
+
raw_result = tool(**args)
|
|
1991
|
+
if self.mask_tool_output:
|
|
1992
|
+
self._secure_result_store[tool_call_id] = raw_result
|
|
1993
|
+
result = (
|
|
1994
|
+
"[The tool has been executed successfully, but the output"
|
|
1995
|
+
" from the tool is masked. You can move forward]"
|
|
1996
|
+
)
|
|
1997
|
+
mask_flag = True
|
|
1998
|
+
else:
|
|
1999
|
+
result = raw_result
|
|
2000
|
+
mask_flag = False
|
|
1962
2001
|
except Exception as e:
|
|
1963
2002
|
# Capture the error message to prevent framework crash
|
|
1964
2003
|
error_msg = f"Error executing tool '{func_name}': {e!s}"
|
|
1965
|
-
result =
|
|
2004
|
+
result = f"Tool execution failed: {error_msg}"
|
|
2005
|
+
mask_flag = False
|
|
1966
2006
|
logging.warning(error_msg)
|
|
1967
2007
|
|
|
1968
|
-
return self._record_tool_calling(
|
|
2008
|
+
return self._record_tool_calling(
|
|
2009
|
+
func_name, args, result, tool_call_id, mask_output=mask_flag
|
|
2010
|
+
)
|
|
1969
2011
|
|
|
1970
2012
|
async def _aexecute_tool(
|
|
1971
2013
|
self,
|
|
@@ -2015,9 +2057,23 @@ class ChatAgent(BaseAgent):
|
|
|
2015
2057
|
args: Dict[str, Any],
|
|
2016
2058
|
result: Any,
|
|
2017
2059
|
tool_call_id: str,
|
|
2060
|
+
mask_output: bool = False,
|
|
2018
2061
|
):
|
|
2019
2062
|
r"""Record the tool calling information in the memory, and return the
|
|
2020
2063
|
tool calling record.
|
|
2064
|
+
|
|
2065
|
+
Args:
|
|
2066
|
+
func_name (str): The name of the tool function called.
|
|
2067
|
+
args (Dict[str, Any]): The arguments passed to the tool.
|
|
2068
|
+
result (Any): The result returned by the tool execution.
|
|
2069
|
+
tool_call_id (str): A unique identifier for the tool call.
|
|
2070
|
+
mask_output (bool, optional): Whether to return a sanitized
|
|
2071
|
+
placeholder instead of the raw tool output.
|
|
2072
|
+
(default: :obj:`False`)
|
|
2073
|
+
|
|
2074
|
+
Returns:
|
|
2075
|
+
ToolCallingRecord: A struct containing information about
|
|
2076
|
+
this tool call.
|
|
2021
2077
|
"""
|
|
2022
2078
|
assist_msg = FunctionCallingMessage(
|
|
2023
2079
|
role_name=self.role_name,
|
|
@@ -2036,6 +2092,7 @@ class ChatAgent(BaseAgent):
|
|
|
2036
2092
|
func_name=func_name,
|
|
2037
2093
|
result=result,
|
|
2038
2094
|
tool_call_id=tool_call_id,
|
|
2095
|
+
mask_output=mask_output,
|
|
2039
2096
|
)
|
|
2040
2097
|
|
|
2041
2098
|
# Use precise timestamps to ensure correct ordering
|
|
@@ -2140,6 +2197,7 @@ class ChatAgent(BaseAgent):
|
|
|
2140
2197
|
),
|
|
2141
2198
|
max_iteration=self.max_iteration,
|
|
2142
2199
|
stop_event=self.stop_event,
|
|
2200
|
+
pause_event=self.pause_event,
|
|
2143
2201
|
)
|
|
2144
2202
|
|
|
2145
2203
|
# Copy memory if requested
|
camel/messages/func_message.py
CHANGED
|
@@ -47,12 +47,16 @@ class FunctionCallingMessage(BaseMessage):
|
|
|
47
47
|
(default: :obj:`None`)
|
|
48
48
|
tool_call_id (Optional[str]): The ID of the tool call, if available.
|
|
49
49
|
(default: :obj:`None`)
|
|
50
|
+
mask_output (Optional[bool]): Whether to return a sanitized placeholder
|
|
51
|
+
instead of the raw tool output.
|
|
52
|
+
(default: :obj:`False`)
|
|
50
53
|
"""
|
|
51
54
|
|
|
52
55
|
func_name: Optional[str] = None
|
|
53
56
|
args: Optional[Dict] = None
|
|
54
57
|
result: Optional[Any] = None
|
|
55
58
|
tool_call_id: Optional[str] = None
|
|
59
|
+
mask_output: Optional[bool] = False
|
|
56
60
|
|
|
57
61
|
def to_openai_message(
|
|
58
62
|
self,
|
|
@@ -105,10 +109,13 @@ class FunctionCallingMessage(BaseMessage):
|
|
|
105
109
|
# This is a function response
|
|
106
110
|
# TODO: Allow for more flexible setting of tool role,
|
|
107
111
|
# optionally to be the same as assistant messages
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
if self.mask_output:
|
|
113
|
+
content = "[MASKED]"
|
|
114
|
+
else:
|
|
115
|
+
content = function_format.format_tool_response(
|
|
116
|
+
self.func_name, # type: ignore[arg-type]
|
|
117
|
+
self.result, # type: ignore[arg-type]
|
|
118
|
+
)
|
|
112
119
|
return ShareGPTMessage(from_="tool", value=content) # type: ignore[call-arg]
|
|
113
120
|
|
|
114
121
|
def to_openai_assistant_message(self) -> OpenAIAssistantMessage:
|
|
@@ -154,10 +161,30 @@ class FunctionCallingMessage(BaseMessage):
|
|
|
154
161
|
" due to missing function name."
|
|
155
162
|
)
|
|
156
163
|
|
|
157
|
-
|
|
164
|
+
if self.mask_output:
|
|
165
|
+
result_content = "[MASKED]"
|
|
166
|
+
else:
|
|
167
|
+
result_content = str(self.result)
|
|
158
168
|
|
|
159
169
|
return {
|
|
160
170
|
"role": "tool",
|
|
161
171
|
"content": result_content,
|
|
162
172
|
"tool_call_id": self.tool_call_id or "null",
|
|
163
173
|
}
|
|
174
|
+
|
|
175
|
+
def to_dict(self) -> Dict:
|
|
176
|
+
r"""Converts the message to a dictionary.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
dict: The converted dictionary.
|
|
180
|
+
"""
|
|
181
|
+
base = super().to_dict()
|
|
182
|
+
base["func_name"] = self.func_name
|
|
183
|
+
if self.args is not None:
|
|
184
|
+
base["args"] = self.args
|
|
185
|
+
if self.result is not None:
|
|
186
|
+
base["result"] = self.result
|
|
187
|
+
if self.tool_call_id is not None:
|
|
188
|
+
base["tool_call_id"] = self.tool_call_id
|
|
189
|
+
base["mask_output"] = self.mask_output
|
|
190
|
+
return base
|
|
@@ -27,7 +27,7 @@ from camel.societies.workforce.prompts import (
|
|
|
27
27
|
)
|
|
28
28
|
from camel.societies.workforce.utils import TaskResult
|
|
29
29
|
from camel.societies.workforce.worker import Worker
|
|
30
|
-
from camel.tasks.task import Task, TaskState,
|
|
30
|
+
from camel.tasks.task import Task, TaskState, is_task_result_insufficient
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class RolePlayingWorker(Worker):
|
|
@@ -178,14 +178,14 @@ class RolePlayingWorker(Worker):
|
|
|
178
178
|
result_dict = json.loads(response.msg.content)
|
|
179
179
|
task_result = TaskResult(**result_dict)
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
task.result = task_result.content
|
|
182
|
+
|
|
183
|
+
if is_task_result_insufficient(task):
|
|
182
184
|
print(
|
|
183
185
|
f"{Fore.RED}Task {task.id}: Content validation failed - "
|
|
184
186
|
f"task marked as failed{Fore.RESET}"
|
|
185
187
|
)
|
|
186
188
|
return TaskState.FAILED
|
|
187
189
|
|
|
188
|
-
task.result = task_result.content
|
|
189
|
-
|
|
190
190
|
print(f"Task result: {task.result}\n")
|
|
191
191
|
return TaskState.DONE
|
|
@@ -26,7 +26,7 @@ from camel.agents import ChatAgent
|
|
|
26
26
|
from camel.societies.workforce.prompts import PROCESS_TASK_PROMPT
|
|
27
27
|
from camel.societies.workforce.utils import TaskResult
|
|
28
28
|
from camel.societies.workforce.worker import Worker
|
|
29
|
-
from camel.tasks.task import Task, TaskState,
|
|
29
|
+
from camel.tasks.task import Task, TaskState, is_task_result_insufficient
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class AgentPool:
|
|
@@ -43,8 +43,6 @@ class AgentPool:
|
|
|
43
43
|
(default: :obj:`10`)
|
|
44
44
|
auto_scale (bool): Whether to automatically scale the pool size.
|
|
45
45
|
(default: :obj:`True`)
|
|
46
|
-
scale_factor (float): Factor by which to scale the pool when needed.
|
|
47
|
-
(default: :obj:`1.5`)
|
|
48
46
|
idle_timeout (float): Time in seconds after which idle agents are
|
|
49
47
|
removed. (default: :obj:`180.0`)
|
|
50
48
|
"""
|
|
@@ -55,13 +53,11 @@ class AgentPool:
|
|
|
55
53
|
initial_size: int = 1,
|
|
56
54
|
max_size: int = 10,
|
|
57
55
|
auto_scale: bool = True,
|
|
58
|
-
scale_factor: float = 1.5,
|
|
59
56
|
idle_timeout: float = 180.0, # 3 minutes
|
|
60
57
|
):
|
|
61
58
|
self.base_agent = base_agent
|
|
62
59
|
self.max_size = max_size
|
|
63
60
|
self.auto_scale = auto_scale
|
|
64
|
-
self.scale_factor = scale_factor
|
|
65
61
|
self.idle_timeout = idle_timeout
|
|
66
62
|
|
|
67
63
|
# Pool management
|
|
@@ -332,7 +328,7 @@ class SingleAgentWorker(Worker):
|
|
|
332
328
|
# Store the actual token usage for this specific task
|
|
333
329
|
task.additional_info["token_usage"] = {"total_tokens": total_tokens}
|
|
334
330
|
|
|
335
|
-
print(f"======\n{Fore.GREEN}
|
|
331
|
+
print(f"======\n{Fore.GREEN}Response from {self}:{Fore.RESET}")
|
|
336
332
|
|
|
337
333
|
try:
|
|
338
334
|
result_dict = json.loads(response.msg.content)
|
|
@@ -352,14 +348,14 @@ class SingleAgentWorker(Worker):
|
|
|
352
348
|
if task_result.failed:
|
|
353
349
|
return TaskState.FAILED
|
|
354
350
|
|
|
355
|
-
|
|
351
|
+
task.result = task_result.content
|
|
352
|
+
|
|
353
|
+
if is_task_result_insufficient(task):
|
|
356
354
|
print(
|
|
357
355
|
f"{Fore.RED}Task {task.id}: Content validation failed - "
|
|
358
356
|
f"task marked as failed{Fore.RESET}"
|
|
359
357
|
)
|
|
360
358
|
return TaskState.FAILED
|
|
361
|
-
|
|
362
|
-
task.result = task_result.content
|
|
363
359
|
return TaskState.DONE
|
|
364
360
|
|
|
365
361
|
async def _listen_to_channel(self):
|