camel-ai 0.2.0__py3-none-any.whl → 0.2.1__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 +2 -0
- camel/loaders/firecrawl_reader.py +11 -43
- camel/models/mistral_model.py +1 -1
- camel/tasks/task.py +11 -4
- camel/tasks/task_prompt.py +4 -0
- camel/utils/commons.py +8 -1
- camel/workforce/__init__.py +6 -6
- camel/workforce/base.py +9 -5
- camel/workforce/prompts.py +175 -0
- camel/workforce/role_playing_worker.py +181 -0
- camel/workforce/{single_agent_node.py → single_agent_worker.py} +49 -22
- camel/workforce/task_channel.py +3 -5
- camel/workforce/utils.py +20 -50
- camel/workforce/{worker_node.py → worker.py} +15 -12
- camel/workforce/workforce.py +456 -19
- {camel_ai-0.2.0.dist-info → camel_ai-0.2.1.dist-info}/METADATA +2 -2
- {camel_ai-0.2.0.dist-info → camel_ai-0.2.1.dist-info}/RECORD +19 -20
- camel/workforce/manager_node.py +0 -299
- camel/workforce/role_playing_node.py +0 -168
- camel/workforce/workforce_prompt.py +0 -125
- {camel_ai-0.2.0.dist-info → camel_ai-0.2.1.dist-info}/WHEEL +0 -0
camel/workforce/manager_node.py
DELETED
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
# =========== Copyright 2023 @ 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 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
|
-
from __future__ import annotations
|
|
15
|
-
|
|
16
|
-
import asyncio
|
|
17
|
-
from collections import deque
|
|
18
|
-
from typing import Deque, Dict, List, Optional
|
|
19
|
-
|
|
20
|
-
from colorama import Fore
|
|
21
|
-
|
|
22
|
-
from camel.agents import ChatAgent
|
|
23
|
-
from camel.messages.base import BaseMessage
|
|
24
|
-
from camel.tasks.task import Task, TaskState
|
|
25
|
-
from camel.workforce.base import BaseNode
|
|
26
|
-
from camel.workforce.single_agent_node import SingleAgentNode
|
|
27
|
-
from camel.workforce.task_channel import TaskChannel
|
|
28
|
-
from camel.workforce.utils import (
|
|
29
|
-
check_if_running,
|
|
30
|
-
parse_assign_task_resp,
|
|
31
|
-
parse_create_node_resp,
|
|
32
|
-
)
|
|
33
|
-
from camel.workforce.worker_node import WorkerNode
|
|
34
|
-
from camel.workforce.workforce_prompt import (
|
|
35
|
-
ASSIGN_TASK_PROMPT,
|
|
36
|
-
CREATE_NODE_PROMPT,
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class ManagerNode(BaseNode):
|
|
41
|
-
r"""A node that manages multiple nodes. It will split the task it
|
|
42
|
-
receives into subtasks and assign them to the child nodes under
|
|
43
|
-
it, and also handles the situation when the task fails.
|
|
44
|
-
|
|
45
|
-
Args:
|
|
46
|
-
description (str): Description of the node.
|
|
47
|
-
coordinator_agent_kwargs (Optional[Dict]): Keyword arguments for the
|
|
48
|
-
coordinator agent, e.g. `model`, `api_key`, `tools`, etc.
|
|
49
|
-
task_agent_kwargs (Optional[Dict]): Keyword arguments for the task
|
|
50
|
-
agent, e.g. `model`, `api_key`, `tools`, etc.
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
def __init__(
|
|
54
|
-
self,
|
|
55
|
-
description: str,
|
|
56
|
-
children: List[BaseNode],
|
|
57
|
-
coordinator_agent_kwargs: Optional[Dict] = None,
|
|
58
|
-
task_agent_kwargs: Optional[Dict] = None,
|
|
59
|
-
) -> None:
|
|
60
|
-
super().__init__(description)
|
|
61
|
-
self._child_listening_tasks: Deque[asyncio.Task] = deque()
|
|
62
|
-
self._children = children
|
|
63
|
-
|
|
64
|
-
coord_agent_sysmsg = BaseMessage.make_assistant_message(
|
|
65
|
-
role_name="Workforce Manager",
|
|
66
|
-
content="You are coordinating a group of workers. A worker can be "
|
|
67
|
-
"a group of agents or a single agent. Each worker is created to"
|
|
68
|
-
" solve a specific kind of task. Your job includes assigning "
|
|
69
|
-
"tasks to a existing worker, creating a new worker for a task, "
|
|
70
|
-
"etc.",
|
|
71
|
-
)
|
|
72
|
-
self.coordinator_agent = ChatAgent(
|
|
73
|
-
coord_agent_sysmsg, **(coordinator_agent_kwargs or {})
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
task_sys_msg = BaseMessage.make_assistant_message(
|
|
77
|
-
role_name="Task Planner",
|
|
78
|
-
content="You are going to compose and decompose tasks.",
|
|
79
|
-
)
|
|
80
|
-
self.task_agent = ChatAgent(task_sys_msg, **(task_agent_kwargs or {}))
|
|
81
|
-
|
|
82
|
-
# if there is one, will set by the workforce class wrapping this
|
|
83
|
-
self._task: Optional[Task] = None
|
|
84
|
-
self._pending_tasks: Deque[Task] = deque()
|
|
85
|
-
|
|
86
|
-
@check_if_running(False)
|
|
87
|
-
def set_main_task(self, task: Task) -> None:
|
|
88
|
-
r"""Set the main task for the node."""
|
|
89
|
-
self._task = task
|
|
90
|
-
|
|
91
|
-
def _get_child_nodes_info(self) -> str:
|
|
92
|
-
r"""Get the information of all the child nodes under this node."""
|
|
93
|
-
return '\n'.join(
|
|
94
|
-
f'{child.node_id}: {child.description}' for child in self._children
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
def _find_assignee(
|
|
98
|
-
self,
|
|
99
|
-
task: Task,
|
|
100
|
-
failed_log: Optional[str] = None,
|
|
101
|
-
) -> str:
|
|
102
|
-
r"""Assigns a task to a child node if capable, otherwise create a
|
|
103
|
-
new worker node.
|
|
104
|
-
|
|
105
|
-
Parameters:
|
|
106
|
-
task (Task): The task to be assigned.
|
|
107
|
-
failed_log (Optional[str]): Optional log of a previous failed
|
|
108
|
-
attempt.
|
|
109
|
-
|
|
110
|
-
Returns:
|
|
111
|
-
str: ID of the assigned node.
|
|
112
|
-
"""
|
|
113
|
-
prompt = ASSIGN_TASK_PROMPT.format(
|
|
114
|
-
content=task.content,
|
|
115
|
-
child_nodes_info=self._get_child_nodes_info(),
|
|
116
|
-
)
|
|
117
|
-
req = BaseMessage.make_user_message(
|
|
118
|
-
role_name="User",
|
|
119
|
-
content=prompt,
|
|
120
|
-
)
|
|
121
|
-
response = self.coordinator_agent.step(req)
|
|
122
|
-
try:
|
|
123
|
-
print(f"{Fore.YELLOW}{response.msg.content}{Fore.RESET}")
|
|
124
|
-
assignee_id = parse_assign_task_resp(response.msg.content)
|
|
125
|
-
except ValueError:
|
|
126
|
-
assignee_id = self._create_worker_node_for_task(task).node_id
|
|
127
|
-
return assignee_id
|
|
128
|
-
|
|
129
|
-
async def _post_task(self, task: Task, assignee_id: str) -> None:
|
|
130
|
-
await self._channel.post_task(task, self.node_id, assignee_id)
|
|
131
|
-
|
|
132
|
-
async def _post_dependency(self, dependency: Task) -> None:
|
|
133
|
-
await self._channel.post_dependency(dependency, self.node_id)
|
|
134
|
-
|
|
135
|
-
def _create_worker_node_for_task(self, task: Task) -> WorkerNode:
|
|
136
|
-
r"""Creates a new worker node for a given task and add it to the
|
|
137
|
-
children list of this node. This is one of the actions that
|
|
138
|
-
the coordinator can take when a task has failed.
|
|
139
|
-
|
|
140
|
-
Args:
|
|
141
|
-
task (Task): The task for which the worker node is created.
|
|
142
|
-
|
|
143
|
-
Returns:
|
|
144
|
-
WorkerNode: The created worker node.
|
|
145
|
-
"""
|
|
146
|
-
prompt = CREATE_NODE_PROMPT.format(
|
|
147
|
-
content=task.content,
|
|
148
|
-
child_nodes_info=self._get_child_nodes_info(),
|
|
149
|
-
)
|
|
150
|
-
req = BaseMessage.make_user_message(
|
|
151
|
-
role_name="User",
|
|
152
|
-
content=prompt,
|
|
153
|
-
)
|
|
154
|
-
response = self.coordinator_agent.step(req)
|
|
155
|
-
new_node_conf = parse_create_node_resp(response.msg.content)
|
|
156
|
-
|
|
157
|
-
worker_sysmsg = BaseMessage.make_assistant_message(
|
|
158
|
-
role_name=new_node_conf.role,
|
|
159
|
-
content=new_node_conf.system,
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
# TODO: add a default selection of tools for the worker
|
|
163
|
-
worker = ChatAgent(worker_sysmsg)
|
|
164
|
-
|
|
165
|
-
new_node = SingleAgentNode(
|
|
166
|
-
description=new_node_conf.description,
|
|
167
|
-
worker=worker,
|
|
168
|
-
)
|
|
169
|
-
new_node.set_channel(self._channel)
|
|
170
|
-
|
|
171
|
-
print(
|
|
172
|
-
f"{Fore.GREEN}New worker node {new_node.node_id} created."
|
|
173
|
-
f"{Fore.RESET}"
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
self._children.append(new_node)
|
|
177
|
-
self._child_listening_tasks.append(
|
|
178
|
-
asyncio.create_task(new_node.start())
|
|
179
|
-
)
|
|
180
|
-
return new_node
|
|
181
|
-
|
|
182
|
-
async def _get_returned_task(self) -> Task:
|
|
183
|
-
r"""Get the task that's published by this node and just get returned
|
|
184
|
-
from the assignee.
|
|
185
|
-
"""
|
|
186
|
-
return await self._channel.get_returned_task_by_publisher(self.node_id)
|
|
187
|
-
|
|
188
|
-
async def _post_ready_tasks(self) -> None:
|
|
189
|
-
r"""Send all the pending tasks that have all the dependencies met to
|
|
190
|
-
the channel, or directly return if there is none. For now, we will
|
|
191
|
-
directly send the first task in the pending list because all the tasks
|
|
192
|
-
are linearly dependent."""
|
|
193
|
-
|
|
194
|
-
if not self._pending_tasks:
|
|
195
|
-
return
|
|
196
|
-
|
|
197
|
-
ready_task = self._pending_tasks[0]
|
|
198
|
-
|
|
199
|
-
# if the task has failed previously, just compose and send the task
|
|
200
|
-
# to the channel as a dependency
|
|
201
|
-
if ready_task.state == TaskState.FAILED:
|
|
202
|
-
# TODO: the composing of tasks seems not work very well
|
|
203
|
-
ready_task.compose(self.task_agent)
|
|
204
|
-
# remove the subtasks from the channel
|
|
205
|
-
for subtask in ready_task.subtasks:
|
|
206
|
-
await self._channel.remove_task(subtask.id)
|
|
207
|
-
# send the task to the channel as a dependency
|
|
208
|
-
await self._post_dependency(ready_task)
|
|
209
|
-
self._pending_tasks.popleft()
|
|
210
|
-
# try to send the next task in the pending list
|
|
211
|
-
await self._post_ready_tasks()
|
|
212
|
-
else:
|
|
213
|
-
# directly post the task to the channel if it's a new one
|
|
214
|
-
# find a node to assign the task
|
|
215
|
-
assignee_id = self._find_assignee(task=ready_task)
|
|
216
|
-
await self._post_task(ready_task, assignee_id)
|
|
217
|
-
|
|
218
|
-
async def _handle_failed_task(self, task: Task) -> None:
|
|
219
|
-
# remove the failed task from the channel
|
|
220
|
-
await self._channel.remove_task(task.id)
|
|
221
|
-
if task.get_depth() >= 3:
|
|
222
|
-
# create a new WF and reassign
|
|
223
|
-
# TODO: add a state for reassign?
|
|
224
|
-
assignee = self._create_worker_node_for_task(task)
|
|
225
|
-
# print('create_new_assignee:', assignee)
|
|
226
|
-
await self._post_task(task, assignee.node_id)
|
|
227
|
-
else:
|
|
228
|
-
subtasks = task.decompose(self.task_agent)
|
|
229
|
-
# Insert packets at the head of the queue
|
|
230
|
-
self._pending_tasks.extendleft(reversed(subtasks))
|
|
231
|
-
await self._post_ready_tasks()
|
|
232
|
-
|
|
233
|
-
async def _handle_completed_task(self, task: Task) -> None:
|
|
234
|
-
# archive the packet, making it into a dependency
|
|
235
|
-
self._pending_tasks.popleft()
|
|
236
|
-
await self._channel.archive_task(task.id)
|
|
237
|
-
await self._post_ready_tasks()
|
|
238
|
-
|
|
239
|
-
@check_if_running(False)
|
|
240
|
-
def set_channel(self, channel: TaskChannel):
|
|
241
|
-
r"""Set the channel for the node and all the child nodes under it."""
|
|
242
|
-
self._channel = channel
|
|
243
|
-
for child in self._children:
|
|
244
|
-
child.set_channel(channel)
|
|
245
|
-
|
|
246
|
-
@check_if_running(False)
|
|
247
|
-
async def _listen_to_channel(self) -> None:
|
|
248
|
-
r"""Continuously listen to the channel, post task to the channel and
|
|
249
|
-
track the status of posted tasks.
|
|
250
|
-
"""
|
|
251
|
-
|
|
252
|
-
self._running = True
|
|
253
|
-
print(f"{Fore.GREEN}Manager node {self.node_id} started.{Fore.RESET}")
|
|
254
|
-
|
|
255
|
-
# if this node is at the top level, it will have an initial task
|
|
256
|
-
# the initial task must be decomposed into subtasks first
|
|
257
|
-
if self._task is not None:
|
|
258
|
-
subtasks = self._task.decompose(self.task_agent)
|
|
259
|
-
self._pending_tasks.extend(subtasks)
|
|
260
|
-
self._task.state = TaskState.FAILED
|
|
261
|
-
self._pending_tasks.append(self._task)
|
|
262
|
-
|
|
263
|
-
# before starting the loop, send ready pending tasks to the channel
|
|
264
|
-
await self._post_ready_tasks()
|
|
265
|
-
|
|
266
|
-
await self._channel.print_channel()
|
|
267
|
-
|
|
268
|
-
while self._task is None or self._pending_tasks:
|
|
269
|
-
returned_task = await self._get_returned_task()
|
|
270
|
-
if returned_task.state == TaskState.DONE:
|
|
271
|
-
await self._handle_completed_task(returned_task)
|
|
272
|
-
elif returned_task.state == TaskState.FAILED:
|
|
273
|
-
await self._handle_failed_task(returned_task)
|
|
274
|
-
else:
|
|
275
|
-
raise ValueError(
|
|
276
|
-
f"Task {returned_task.id} has an unexpected state."
|
|
277
|
-
)
|
|
278
|
-
|
|
279
|
-
# shut down the whole workforce tree
|
|
280
|
-
self.stop()
|
|
281
|
-
|
|
282
|
-
@check_if_running(False)
|
|
283
|
-
async def start(self) -> None:
|
|
284
|
-
r"""Start itself and all the child nodes under it."""
|
|
285
|
-
for child in self._children:
|
|
286
|
-
child_listening_task = asyncio.create_task(child.start())
|
|
287
|
-
self._child_listening_tasks.append(child_listening_task)
|
|
288
|
-
await self._listen_to_channel()
|
|
289
|
-
|
|
290
|
-
@check_if_running(True)
|
|
291
|
-
def stop(self) -> None:
|
|
292
|
-
r"""Stop all the child nodes under it. The node itself will be stopped
|
|
293
|
-
by its parent node.
|
|
294
|
-
"""
|
|
295
|
-
for child in self._children:
|
|
296
|
-
child.stop()
|
|
297
|
-
for child_task in self._child_listening_tasks:
|
|
298
|
-
child_task.cancel()
|
|
299
|
-
self._running = False
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
# =========== Copyright 2023 @ 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 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
|
-
from __future__ import annotations
|
|
15
|
-
|
|
16
|
-
from typing import Dict, List, Optional
|
|
17
|
-
|
|
18
|
-
from colorama import Fore, Style
|
|
19
|
-
|
|
20
|
-
from camel.agents.chat_agent import ChatAgent, FunctionCallingRecord
|
|
21
|
-
from camel.messages.base import BaseMessage
|
|
22
|
-
from camel.societies import RolePlaying
|
|
23
|
-
from camel.tasks.task import Task, TaskState
|
|
24
|
-
from camel.utils import print_text_animated
|
|
25
|
-
from camel.workforce.utils import parse_task_result_resp
|
|
26
|
-
from camel.workforce.worker_node import WorkerNode
|
|
27
|
-
from camel.workforce.workforce_prompt import (
|
|
28
|
-
ROLEPLAY_PROCESS_TASK_PROMPT,
|
|
29
|
-
ROLEPLAY_SUMMERIZE_PROMPT,
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class RolePlayingNode(WorkerNode):
|
|
34
|
-
r"""A worker node that contains a role playing.
|
|
35
|
-
|
|
36
|
-
Args:
|
|
37
|
-
description (str): Description of the node.
|
|
38
|
-
assistant_role_name (str): The role name of the assistant agent.
|
|
39
|
-
user_role_name (str): The role name of the user agent.
|
|
40
|
-
assistant_agent_kwargs (Optional[Dict], optional): The keyword
|
|
41
|
-
arguments to initialize the assistant agent in the role playing,
|
|
42
|
-
like the model name, etc. Defaults to None.
|
|
43
|
-
user_agent_kwargs (Optional[Dict], optional): The keyword arguments to
|
|
44
|
-
initialize the user agent in the role playing, like the model name,
|
|
45
|
-
etc. Defaults to None.
|
|
46
|
-
chat_turn_limit (int, optional): The maximum number of chat turns in
|
|
47
|
-
the role playing. Defaults to 3.
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
def __init__(
|
|
51
|
-
self,
|
|
52
|
-
description: str,
|
|
53
|
-
assistant_role_name: str,
|
|
54
|
-
user_role_name: str,
|
|
55
|
-
assistant_agent_kwargs: Optional[Dict] = None,
|
|
56
|
-
user_agent_kwargs: Optional[Dict] = None,
|
|
57
|
-
chat_turn_limit: int = 3,
|
|
58
|
-
) -> None:
|
|
59
|
-
super().__init__(description)
|
|
60
|
-
sys_message = BaseMessage.make_assistant_message(
|
|
61
|
-
role_name="Summarizer",
|
|
62
|
-
content="Good at summarizing the results of the chat logs",
|
|
63
|
-
)
|
|
64
|
-
self.summerize_agent = ChatAgent(sys_message)
|
|
65
|
-
self.chat_turn_limit = chat_turn_limit
|
|
66
|
-
self.assistant_role_name = assistant_role_name
|
|
67
|
-
self.user_role_name = user_role_name
|
|
68
|
-
self.assistant_agent_kwargs = assistant_agent_kwargs
|
|
69
|
-
self.user_agent_kwargs = user_agent_kwargs
|
|
70
|
-
|
|
71
|
-
async def _process_task(
|
|
72
|
-
self, task: Task, dependencies: List[Task]
|
|
73
|
-
) -> TaskState:
|
|
74
|
-
r"""Processes a task leveraging its dependencies through role-playing.
|
|
75
|
-
|
|
76
|
-
This method orchestrates a role-playing session between an AI
|
|
77
|
-
assistant and an AI user to process a given task. It initiates with a
|
|
78
|
-
generated prompt based on the task and its dependencies, conducts a
|
|
79
|
-
dialogue up to a specified chat turn limit, and then summarizes the
|
|
80
|
-
dialogue to determine the task's outcome.
|
|
81
|
-
|
|
82
|
-
Args:
|
|
83
|
-
task (Task): The task object to be processed, containing necessary
|
|
84
|
-
details like content and type.
|
|
85
|
-
dependencies (List[Task]): A list of task objects that the current
|
|
86
|
-
task depends on.
|
|
87
|
-
|
|
88
|
-
Returns:
|
|
89
|
-
TaskState: `TaskState.DONE` if processed successfully, otherwise
|
|
90
|
-
`TaskState.FAILED`.
|
|
91
|
-
"""
|
|
92
|
-
try:
|
|
93
|
-
dependency_tasks_info = self._get_dep_tasks_info(dependencies)
|
|
94
|
-
prompt = ROLEPLAY_PROCESS_TASK_PROMPT.format(
|
|
95
|
-
content=task.content,
|
|
96
|
-
type=task.type,
|
|
97
|
-
dependency_task_info=dependency_tasks_info,
|
|
98
|
-
)
|
|
99
|
-
role_play_session = RolePlaying(
|
|
100
|
-
assistant_role_name=self.assistant_role_name,
|
|
101
|
-
user_role_name=self.user_role_name,
|
|
102
|
-
assistant_agent_kwargs=self.assistant_agent_kwargs,
|
|
103
|
-
user_agent_kwargs=self.user_agent_kwargs,
|
|
104
|
-
task_prompt=prompt,
|
|
105
|
-
with_task_specify=False,
|
|
106
|
-
)
|
|
107
|
-
n = 0
|
|
108
|
-
input_msg = role_play_session.init_chat()
|
|
109
|
-
chat_history = []
|
|
110
|
-
while n < self.chat_turn_limit:
|
|
111
|
-
n += 1
|
|
112
|
-
assistant_response, user_response = role_play_session.step(
|
|
113
|
-
input_msg
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
if assistant_response.terminated:
|
|
117
|
-
reason = assistant_response.info['termination_reasons']
|
|
118
|
-
print(
|
|
119
|
-
Fore.GREEN + f"AI Assistant terminated. Reason: "
|
|
120
|
-
f"{reason}. "
|
|
121
|
-
)
|
|
122
|
-
break
|
|
123
|
-
if user_response.terminated:
|
|
124
|
-
reason = user_response.info['termination_reasons']
|
|
125
|
-
print(
|
|
126
|
-
Fore.GREEN + f"AI User terminated. Reason: {reason}."
|
|
127
|
-
)
|
|
128
|
-
break
|
|
129
|
-
print_text_animated(
|
|
130
|
-
Fore.BLUE + f"AI User:\n\n{user_response.msg.content}\n",
|
|
131
|
-
delay=0.005,
|
|
132
|
-
)
|
|
133
|
-
chat_history.append(f"AI User: {user_response.msg.content}")
|
|
134
|
-
|
|
135
|
-
print_text_animated(Fore.GREEN + "AI Assistant:", delay=0.005)
|
|
136
|
-
tool_calls: List[FunctionCallingRecord] = (
|
|
137
|
-
assistant_response.info['tool_calls']
|
|
138
|
-
)
|
|
139
|
-
for func_record in tool_calls:
|
|
140
|
-
print_text_animated(f"{func_record}", delay=0.005)
|
|
141
|
-
print_text_animated(
|
|
142
|
-
f"{assistant_response.msg.content}\n", delay=0.005
|
|
143
|
-
)
|
|
144
|
-
chat_history.append(
|
|
145
|
-
f"AI Assistant: {assistant_response.msg.content}"
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
if "CAMEL_TASK_DONE" in user_response.msg.content:
|
|
149
|
-
break
|
|
150
|
-
|
|
151
|
-
input_msg = assistant_response.msg
|
|
152
|
-
|
|
153
|
-
chat_history_str = "\n".join(chat_history)
|
|
154
|
-
prompt = ROLEPLAY_SUMMERIZE_PROMPT.format(
|
|
155
|
-
content=task.content,
|
|
156
|
-
type=task.type,
|
|
157
|
-
chat_history=chat_history_str,
|
|
158
|
-
)
|
|
159
|
-
req = BaseMessage.make_user_message(
|
|
160
|
-
role_name="User",
|
|
161
|
-
content=prompt,
|
|
162
|
-
)
|
|
163
|
-
response = self.summerize_agent.step(req)
|
|
164
|
-
task.result = parse_task_result_resp(response.msg.content)
|
|
165
|
-
print(Style.RESET_ALL + 'Task result:', task.result, '\n')
|
|
166
|
-
return TaskState.DONE
|
|
167
|
-
except Exception:
|
|
168
|
-
return TaskState.FAILED
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
# =========== Copyright 2023 @ 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 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
|
-
from camel.prompts import TextPrompt
|
|
15
|
-
|
|
16
|
-
# ruff: noqa: E501
|
|
17
|
-
CREATE_NODE_PROMPT = TextPrompt(
|
|
18
|
-
"""You need to use the given information to create a new workforce for solving the category of tasks of the given one.
|
|
19
|
-
The content of the given task is:
|
|
20
|
-
|
|
21
|
-
{content}
|
|
22
|
-
|
|
23
|
-
Following is the information of the existing workforces. The format is <ID>: <description>.
|
|
24
|
-
|
|
25
|
-
{child_nodes_info}
|
|
26
|
-
|
|
27
|
-
You must return the following information:
|
|
28
|
-
1. The role of the agent working in the workforce, e.g. "programmer", "researcher", "product owner". This should be enclosed within the <role></role> tags.
|
|
29
|
-
2. The system message that will be sent to the agent, enclosed within the <system></system> tags.
|
|
30
|
-
3. The description of the workforce, enclosed within the <description></description> tags.
|
|
31
|
-
|
|
32
|
-
Also, all of the info should be enclosed within a <workforce> tag. For example:
|
|
33
|
-
|
|
34
|
-
<workforce>
|
|
35
|
-
<role>programmer</role>
|
|
36
|
-
<system>You are a python programmer.</system>
|
|
37
|
-
<description>a python programmer.</description>
|
|
38
|
-
</workforce>
|
|
39
|
-
|
|
40
|
-
You should ensure that the workforce created is capable of solving all the tasks in the same category as the given one, don't make it too specific.
|
|
41
|
-
Also, there should be no big overlap between the new workforce and the existing ones.
|
|
42
|
-
The information returned should be concise and clear. Each pair of tag can only appear once.
|
|
43
|
-
"""
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
ASSIGN_TASK_PROMPT = TextPrompt(
|
|
47
|
-
"""You need to assign the task to a workforce.
|
|
48
|
-
The content of the task is:
|
|
49
|
-
|
|
50
|
-
{content}
|
|
51
|
-
|
|
52
|
-
Following is the information of the existing workforces. The format is <ID>: <description>.
|
|
53
|
-
|
|
54
|
-
{child_nodes_info}
|
|
55
|
-
|
|
56
|
-
You must return the ID of the workforce that you think is most capable of doing the task. The ID should be enclosed within the <id></id> tags, for example:
|
|
57
|
-
|
|
58
|
-
<id>1</id>
|
|
59
|
-
"""
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
PROCESS_TASK_PROMPT = TextPrompt(
|
|
63
|
-
"""You need to process the task. It is recommended that tools be actively called when needed.
|
|
64
|
-
The content of the task is:
|
|
65
|
-
|
|
66
|
-
{content}
|
|
67
|
-
|
|
68
|
-
The type of the task is:
|
|
69
|
-
|
|
70
|
-
{type}
|
|
71
|
-
|
|
72
|
-
To process this task, here is some information on several dependent tasks:
|
|
73
|
-
|
|
74
|
-
{dependency_task_info}
|
|
75
|
-
|
|
76
|
-
You must return the result of the given task. The result should be enclosed within the <result></result> tags, for example:
|
|
77
|
-
|
|
78
|
-
<result>Today, you requested information about the current weather conditions. The weather today in New York City is partly cloudy with a high of 75°F (24°C) and a low of 59°F (15°C). There is a 10 percent chance of rain with winds coming from the northeast at 5 mph. Humidity levels are at 60%. It's a perfect day to spend some time outdoors, perhaps in one of the city's beautiful parks.</result>
|
|
79
|
-
|
|
80
|
-
if you are not able to process the task, you need to return <failed></failed> tags.
|
|
81
|
-
"""
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
ROLEPLAY_PROCESS_TASK_PROMPT = TextPrompt(
|
|
86
|
-
"""You need to process the task. It is recommended that tools be actively called when needed.
|
|
87
|
-
The content of the task is:
|
|
88
|
-
|
|
89
|
-
{content}
|
|
90
|
-
|
|
91
|
-
The type of the task is:
|
|
92
|
-
|
|
93
|
-
{type}
|
|
94
|
-
|
|
95
|
-
To process this task, here is some information on several dependent tasks:
|
|
96
|
-
|
|
97
|
-
{dependency_task_info}
|
|
98
|
-
|
|
99
|
-
You must return the result of the given task.
|
|
100
|
-
|
|
101
|
-
"""
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
# TODO: improve prompt quality
|
|
105
|
-
ROLEPLAY_SUMMERIZE_PROMPT = TextPrompt(
|
|
106
|
-
"""You need to process the task.
|
|
107
|
-
The content of the task is:
|
|
108
|
-
|
|
109
|
-
{content}
|
|
110
|
-
|
|
111
|
-
The type of the task is:
|
|
112
|
-
|
|
113
|
-
{type}
|
|
114
|
-
|
|
115
|
-
To process this task, here are the chat history of AI user and AI assistant:
|
|
116
|
-
|
|
117
|
-
{chat_history}
|
|
118
|
-
|
|
119
|
-
You must summerize the chat history of the return the result of the given task. The result should be enclosed within the <result></result> tags, for example:
|
|
120
|
-
|
|
121
|
-
<result>Today, you requested information about the current weather conditions. The weather today in New York City is partly cloudy with a high of 75°F (24°C) and a low of 59°F (15°C). There is a 10 percent chance of rain with winds coming from the northeast at 5 mph. Humidity levels are at 60%. It's a perfect day to spend some time outdoors, perhaps in one of the city's beautiful parks.</result>
|
|
122
|
-
|
|
123
|
-
if you are not able to process the task, you need to return <failed></failed> tags.
|
|
124
|
-
"""
|
|
125
|
-
)
|
|
File without changes
|