camel-ai 0.1.5.9__py3-none-any.whl → 0.1.6.2__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 +246 -33
- camel/agents/critic_agent.py +17 -1
- camel/agents/deductive_reasoner_agent.py +12 -0
- camel/agents/embodied_agent.py +19 -5
- camel/agents/knowledge_graph_agent.py +22 -3
- camel/agents/role_assignment_agent.py +12 -0
- camel/agents/search_agent.py +12 -0
- camel/agents/task_agent.py +15 -0
- camel/configs/__init__.py +2 -9
- camel/configs/anthropic_config.py +5 -6
- camel/configs/base_config.py +50 -4
- camel/configs/gemini_config.py +69 -18
- camel/configs/groq_config.py +6 -20
- camel/configs/litellm_config.py +2 -8
- camel/configs/mistral_config.py +17 -20
- camel/configs/ollama_config.py +6 -8
- camel/configs/openai_config.py +12 -23
- camel/configs/vllm_config.py +7 -8
- camel/configs/zhipuai_config.py +5 -11
- camel/human.py +1 -1
- camel/loaders/__init__.py +2 -0
- camel/loaders/firecrawl_reader.py +213 -0
- camel/memories/agent_memories.py +1 -4
- camel/memories/blocks/chat_history_block.py +6 -2
- camel/memories/blocks/vectordb_block.py +3 -1
- camel/memories/context_creators/score_based.py +6 -6
- camel/memories/records.py +9 -7
- camel/messages/base.py +1 -0
- camel/models/open_source_model.py +2 -2
- camel/prompts/__init__.py +7 -0
- camel/prompts/image_craft.py +34 -0
- camel/prompts/multi_condition_image_craft.py +34 -0
- camel/prompts/task_prompt_template.py +6 -0
- camel/responses/agent_responses.py +4 -3
- camel/retrievers/auto_retriever.py +0 -2
- camel/societies/babyagi_playing.py +6 -4
- camel/societies/role_playing.py +16 -8
- camel/storages/graph_storages/graph_element.py +10 -14
- camel/storages/vectordb_storages/base.py +24 -13
- camel/storages/vectordb_storages/milvus.py +1 -1
- camel/storages/vectordb_storages/qdrant.py +2 -3
- camel/tasks/__init__.py +22 -0
- camel/tasks/task.py +408 -0
- camel/tasks/task_prompt.py +65 -0
- camel/toolkits/__init__.py +3 -0
- camel/toolkits/base.py +3 -1
- camel/toolkits/dalle_toolkit.py +146 -0
- camel/toolkits/github_toolkit.py +16 -32
- camel/toolkits/google_maps_toolkit.py +2 -1
- camel/toolkits/open_api_toolkit.py +1 -2
- camel/toolkits/openai_function.py +2 -7
- camel/types/enums.py +6 -2
- camel/utils/__init__.py +14 -2
- camel/utils/commons.py +167 -2
- camel/utils/constants.py +3 -0
- camel/workforce/__init__.py +23 -0
- camel/workforce/base.py +50 -0
- camel/workforce/manager_node.py +299 -0
- camel/workforce/role_playing_node.py +168 -0
- camel/workforce/single_agent_node.py +77 -0
- camel/workforce/task_channel.py +173 -0
- camel/workforce/utils.py +97 -0
- camel/workforce/worker_node.py +115 -0
- camel/workforce/workforce.py +49 -0
- camel/workforce/workforce_prompt.py +125 -0
- {camel_ai-0.1.5.9.dist-info → camel_ai-0.1.6.2.dist-info}/METADATA +5 -2
- {camel_ai-0.1.5.9.dist-info → camel_ai-0.1.6.2.dist-info}/RECORD +69 -52
- {camel_ai-0.1.5.9.dist-info → camel_ai-0.1.6.2.dist-info}/WHEEL +0 -0
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
14
|
|
|
15
15
|
from abc import ABC, abstractmethod
|
|
16
|
-
from dataclasses import dataclass, field
|
|
17
16
|
from typing import Any, Dict, List, Optional
|
|
18
17
|
from uuid import uuid4
|
|
19
18
|
|
|
19
|
+
from pydantic import BaseModel, Field
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
class VectorRecord:
|
|
21
|
+
|
|
22
|
+
class VectorRecord(BaseModel):
|
|
23
23
|
r"""Encapsulates information about a vector's unique identifier and its
|
|
24
24
|
payload, which is primarily used as a data transfer object when saving
|
|
25
25
|
to vector storage.
|
|
@@ -33,12 +33,11 @@ class VectorRecord:
|
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
35
|
vector: List[float]
|
|
36
|
-
id: str =
|
|
36
|
+
id: str = Field(default_factory=lambda: str(uuid4()))
|
|
37
37
|
payload: Optional[Dict[str, Any]] = None
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
class VectorDBQuery:
|
|
40
|
+
class VectorDBQuery(BaseModel):
|
|
42
41
|
r"""Represents a query to a vector database.
|
|
43
42
|
|
|
44
43
|
Attributes:
|
|
@@ -49,11 +48,24 @@ class VectorDBQuery:
|
|
|
49
48
|
"""
|
|
50
49
|
|
|
51
50
|
query_vector: List[float]
|
|
51
|
+
"""The numerical representation of the query vector."""
|
|
52
52
|
top_k: int = 1
|
|
53
|
+
"""The number of top similar vectors to retrieve from the database."""
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self, query_vector: List[float], top_k: int, **kwargs: Any
|
|
57
|
+
) -> None:
|
|
58
|
+
"""Pass in query_vector and tok_k as positional arg.
|
|
59
|
+
Args:
|
|
60
|
+
query_vector (List[float]): The numerical representation of the
|
|
61
|
+
query vector.
|
|
62
|
+
top_k (int, optional): The number of top similar vectors to
|
|
63
|
+
retrieve from the database. (default: :obj:`1`)
|
|
64
|
+
"""
|
|
65
|
+
super().__init__(query_vector=query_vector, top_k=top_k, **kwargs)
|
|
53
66
|
|
|
54
67
|
|
|
55
|
-
|
|
56
|
-
class VectorDBQueryResult:
|
|
68
|
+
class VectorDBQueryResult(BaseModel):
|
|
57
69
|
r"""Encapsulates the result of a query against a vector database.
|
|
58
70
|
|
|
59
71
|
Attributes:
|
|
@@ -66,7 +78,7 @@ class VectorDBQueryResult:
|
|
|
66
78
|
similarity: float
|
|
67
79
|
|
|
68
80
|
@classmethod
|
|
69
|
-
def
|
|
81
|
+
def create(
|
|
70
82
|
cls,
|
|
71
83
|
similarity: float,
|
|
72
84
|
vector: List[float],
|
|
@@ -75,13 +87,12 @@ class VectorDBQueryResult:
|
|
|
75
87
|
) -> "VectorDBQueryResult":
|
|
76
88
|
r"""A class method to construct a `VectorDBQueryResult` instance."""
|
|
77
89
|
return cls(
|
|
78
|
-
record=VectorRecord(vector, id, payload),
|
|
90
|
+
record=VectorRecord(vector=vector, id=id, payload=payload),
|
|
79
91
|
similarity=similarity,
|
|
80
92
|
)
|
|
81
93
|
|
|
82
94
|
|
|
83
|
-
|
|
84
|
-
class VectorDBStatus:
|
|
95
|
+
class VectorDBStatus(BaseModel):
|
|
85
96
|
r"""Vector database status.
|
|
86
97
|
|
|
87
98
|
Attributes:
|
|
@@ -195,7 +206,7 @@ class BaseVectorStorage(ABC):
|
|
|
195
206
|
List[List[Dict[str, Any]]]: A list of vector payloads retrieved
|
|
196
207
|
from the storage based on similarity to the query vector.
|
|
197
208
|
"""
|
|
198
|
-
results = self.query(VectorDBQuery(vector, top_k))
|
|
209
|
+
results = self.query(VectorDBQuery(query_vector=vector, top_k=top_k))
|
|
199
210
|
return [
|
|
200
211
|
result.record.payload
|
|
201
212
|
for result in results
|
|
@@ -361,7 +361,7 @@ class MilvusStorage(BaseVectorStorage):
|
|
|
361
361
|
query_results = []
|
|
362
362
|
for point in search_result:
|
|
363
363
|
query_results.append(
|
|
364
|
-
VectorDBQueryResult.
|
|
364
|
+
VectorDBQueryResult.create(
|
|
365
365
|
similarity=(point[0]['distance']),
|
|
366
366
|
id=str(point[0]['id']),
|
|
367
367
|
payload=(point[0]['entity'].get('payload')),
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
# See the License for the specific language governing permissions and
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
|
-
from dataclasses import asdict
|
|
15
14
|
from datetime import datetime
|
|
16
15
|
from typing import Any, Dict, List, Optional, Tuple, Union, cast
|
|
17
16
|
|
|
@@ -261,7 +260,7 @@ class QdrantStorage(BaseVectorStorage):
|
|
|
261
260
|
"""
|
|
262
261
|
from qdrant_client.http.models import PointStruct, UpdateStatus
|
|
263
262
|
|
|
264
|
-
qdrant_points = [PointStruct(**
|
|
263
|
+
qdrant_points = [PointStruct(**p.model_dump()) for p in records]
|
|
265
264
|
op_info = self._client.upsert(
|
|
266
265
|
collection_name=self.collection_name,
|
|
267
266
|
points=qdrant_points,
|
|
@@ -340,7 +339,7 @@ class QdrantStorage(BaseVectorStorage):
|
|
|
340
339
|
query_results = []
|
|
341
340
|
for point in search_result:
|
|
342
341
|
query_results.append(
|
|
343
|
-
VectorDBQueryResult.
|
|
342
|
+
VectorDBQueryResult.create(
|
|
344
343
|
similarity=point.score,
|
|
345
344
|
id=str(point.id),
|
|
346
345
|
payload=point.payload,
|
camel/tasks/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
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 .task import Task, TaskManager
|
|
15
|
+
from .task_prompt import TASK_DECOMPOSE_PROMPT, TASK_EVOLVE_PROMPT
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"TASK_DECOMPOSE_PROMPT",
|
|
19
|
+
"TASK_EVOLVE_PROMPT",
|
|
20
|
+
"Task",
|
|
21
|
+
"TaskManager",
|
|
22
|
+
]
|
camel/tasks/task.py
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
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
|
+
|
|
15
|
+
import re
|
|
16
|
+
from enum import Enum
|
|
17
|
+
from typing import Callable, Dict, List, Literal, Optional, Union
|
|
18
|
+
|
|
19
|
+
from pydantic import BaseModel
|
|
20
|
+
|
|
21
|
+
from camel.agents import ChatAgent
|
|
22
|
+
from camel.messages import BaseMessage
|
|
23
|
+
from camel.prompts import TextPrompt
|
|
24
|
+
|
|
25
|
+
from .task_prompt import (
|
|
26
|
+
TASK_COMPOSE_PROMPT,
|
|
27
|
+
TASK_DECOMPOSE_PROMPT,
|
|
28
|
+
TASK_EVOLVE_PROMPT,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def parse_response(
|
|
33
|
+
response: str, task_id: Optional[str] = None
|
|
34
|
+
) -> List["Task"]:
|
|
35
|
+
r"""Parse Tasks from a response.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
response (str): The model response.
|
|
39
|
+
task_id (str, optional): a parent task id,
|
|
40
|
+
the default value is "0"
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
List[Task]: A list of tasks which is :obj:`Task` instance.
|
|
44
|
+
"""
|
|
45
|
+
pattern = "<task>(.*?)</task>"
|
|
46
|
+
tasks_content = re.findall(pattern, response, re.DOTALL)
|
|
47
|
+
|
|
48
|
+
tasks = []
|
|
49
|
+
if task_id is None:
|
|
50
|
+
task_id = "0"
|
|
51
|
+
for i, content in enumerate(tasks_content):
|
|
52
|
+
tasks.append(Task(content=content.strip(), id=f"{task_id}.{i}"))
|
|
53
|
+
return tasks
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TaskState(str, Enum):
|
|
57
|
+
OPEN = "OPEN"
|
|
58
|
+
RUNNING = "RUNNING"
|
|
59
|
+
DONE = "DONE"
|
|
60
|
+
FAILED = "FAILED"
|
|
61
|
+
DELETED = "DELETED"
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def states(cls):
|
|
65
|
+
return [s.value for s in cls]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Task(BaseModel):
|
|
69
|
+
r"""Task is specific assignment that can be passed to a agent.
|
|
70
|
+
|
|
71
|
+
Attributes:
|
|
72
|
+
content: string content for task.
|
|
73
|
+
id: An unique string identifier for the task. This should
|
|
74
|
+
ideally be provided by the provider/model which created the task.
|
|
75
|
+
state: The state which should be OPEN, RUNNING, DONE or DELETED.
|
|
76
|
+
type: task type
|
|
77
|
+
parent: The parent task, None for root task.
|
|
78
|
+
subtasks: The childrent sub-tasks for the task.
|
|
79
|
+
result: The answer for the task.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
content: str
|
|
83
|
+
|
|
84
|
+
id: str = ""
|
|
85
|
+
|
|
86
|
+
state: TaskState = TaskState.OPEN
|
|
87
|
+
|
|
88
|
+
type: Optional[str] = None
|
|
89
|
+
|
|
90
|
+
parent: Optional["Task"] = None
|
|
91
|
+
|
|
92
|
+
subtasks: List["Task"] = []
|
|
93
|
+
|
|
94
|
+
result: Optional[str] = ""
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def from_message(cls, message: BaseMessage) -> "Task":
|
|
98
|
+
r"""Create a task from a message.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
message (BaseMessage): The message to the task.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Task
|
|
105
|
+
"""
|
|
106
|
+
return cls(content=message.content, id="0")
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def to_message():
|
|
110
|
+
r"""Convert a Task to a Message."""
|
|
111
|
+
# TODO
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
def reset(self):
|
|
115
|
+
r"""Reset Task to initial state."""
|
|
116
|
+
self.state = TaskState.OPEN
|
|
117
|
+
self.result = ""
|
|
118
|
+
|
|
119
|
+
def update_result(self, result: str):
|
|
120
|
+
r"""Set task result and mark the task as DONE.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
result (str): The task result.
|
|
124
|
+
"""
|
|
125
|
+
self.result = result
|
|
126
|
+
self.set_state(TaskState.DONE)
|
|
127
|
+
|
|
128
|
+
def set_id(self, id: str):
|
|
129
|
+
self.id = id
|
|
130
|
+
|
|
131
|
+
def set_state(self, state: TaskState):
|
|
132
|
+
r"""Recursively set the state of the task and its subtasks.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
state (TaskState): The giving state.
|
|
136
|
+
"""
|
|
137
|
+
self.state = state
|
|
138
|
+
if state == TaskState.DONE:
|
|
139
|
+
for subtask in self.subtasks:
|
|
140
|
+
if subtask.state != TaskState.DELETED:
|
|
141
|
+
subtask.set_state(state)
|
|
142
|
+
elif state == TaskState.RUNNING and self.parent:
|
|
143
|
+
self.parent.set_state(state)
|
|
144
|
+
|
|
145
|
+
def add_subtask(self, task: "Task"):
|
|
146
|
+
task.parent = self
|
|
147
|
+
self.subtasks.append(task)
|
|
148
|
+
|
|
149
|
+
def remove_subtask(self, id: str):
|
|
150
|
+
self.subtasks = [task for task in self.subtasks if task.id != id]
|
|
151
|
+
|
|
152
|
+
def get_running_task(self) -> Optional["Task"]:
|
|
153
|
+
r"""Get RUNNING task."""
|
|
154
|
+
for sub in self.subtasks:
|
|
155
|
+
if sub.state == TaskState.RUNNING:
|
|
156
|
+
return sub.get_running_task()
|
|
157
|
+
if self.state == TaskState.RUNNING:
|
|
158
|
+
return self
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
def to_string(self, indent: str = "", state: bool = False) -> str:
|
|
162
|
+
r"""Convert task to a sting.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
indent (str): The ident for hierarchical tasks.
|
|
166
|
+
state (bool): Include or not task state.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
str: The printable task string.
|
|
170
|
+
"""
|
|
171
|
+
if state:
|
|
172
|
+
_str = f"{indent}[{self.state}] Task {self.id}: {self.content}\n"
|
|
173
|
+
else:
|
|
174
|
+
_str = f"{indent}Task {self.id}: {self.content}\n"
|
|
175
|
+
for subtask in self.subtasks:
|
|
176
|
+
_str += subtask.to_string(indent + " ", state)
|
|
177
|
+
return _str
|
|
178
|
+
|
|
179
|
+
def get_result(self, indent: str = "") -> str:
|
|
180
|
+
r"""Get task result to a sting.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
indent (str): The ident for hierarchical tasks.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
str: The printable task string.
|
|
187
|
+
"""
|
|
188
|
+
_str = f"{indent}Task {self.id} result: {self.result}\n"
|
|
189
|
+
for subtask in self.subtasks:
|
|
190
|
+
_str += subtask.get_result(indent + " ")
|
|
191
|
+
return _str
|
|
192
|
+
|
|
193
|
+
def decompose(
|
|
194
|
+
self,
|
|
195
|
+
agent: ChatAgent,
|
|
196
|
+
template: TextPrompt = TASK_DECOMPOSE_PROMPT,
|
|
197
|
+
task_parser: Callable[[str, str], List["Task"]] = parse_response,
|
|
198
|
+
) -> List["Task"]:
|
|
199
|
+
r"""Decompose a task to a list of sub-tasks. It can be used for data
|
|
200
|
+
generation and planner of agent.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
agent (ChatAgent): An agent that used to decompose the task.
|
|
204
|
+
template (TextPrompt): The prompt template to decompose
|
|
205
|
+
task. If not provided, the default template will be used.
|
|
206
|
+
task_parser (Callable[[str, str], List[Task]], optional): A
|
|
207
|
+
function to extract Task from response. If not provided,
|
|
208
|
+
the default parse_response will be used.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
List[Task]: A list of tasks which are :obj:`Task` instances.
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
role_name = agent.role_name
|
|
215
|
+
content = template.format(
|
|
216
|
+
role_name=role_name,
|
|
217
|
+
content=self.content,
|
|
218
|
+
)
|
|
219
|
+
msg = BaseMessage.make_user_message(
|
|
220
|
+
role_name=role_name, content=content
|
|
221
|
+
)
|
|
222
|
+
response = agent.step(msg)
|
|
223
|
+
tasks = task_parser(response.msg.content, self.id)
|
|
224
|
+
return tasks
|
|
225
|
+
|
|
226
|
+
def compose(
|
|
227
|
+
self,
|
|
228
|
+
agent: ChatAgent,
|
|
229
|
+
template: TextPrompt = TASK_COMPOSE_PROMPT,
|
|
230
|
+
result_parser: Optional[Callable[[str], str]] = None,
|
|
231
|
+
):
|
|
232
|
+
r"""compose task result by the sub-tasks.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
agent (ChatAgent): An agent that used to compose the task result.
|
|
236
|
+
template (TextPrompt, optional): The prompt template to compose
|
|
237
|
+
task. If not provided, the default template will be used.
|
|
238
|
+
result_parser (Callable[[str, str], List[Task]], optional): A
|
|
239
|
+
function to extract Task from response.
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
if not self.subtasks:
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
sub_tasks_result = self.get_result()
|
|
246
|
+
|
|
247
|
+
role_name = agent.role_name
|
|
248
|
+
content = template.format(
|
|
249
|
+
role_name=role_name,
|
|
250
|
+
content=self.content,
|
|
251
|
+
other_results=sub_tasks_result,
|
|
252
|
+
)
|
|
253
|
+
msg = BaseMessage.make_user_message(
|
|
254
|
+
role_name=role_name, content=content
|
|
255
|
+
)
|
|
256
|
+
response = agent.step(msg)
|
|
257
|
+
result = response.msg.content
|
|
258
|
+
if result_parser:
|
|
259
|
+
result = result_parser(result)
|
|
260
|
+
self.update_result(result)
|
|
261
|
+
|
|
262
|
+
def get_depth(self) -> int:
|
|
263
|
+
r"""Get current task depth."""
|
|
264
|
+
if self.parent is None:
|
|
265
|
+
return 1
|
|
266
|
+
return 1 + self.parent.get_depth()
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class TaskManager:
|
|
270
|
+
r"""TaskManager is used to manage tasks.
|
|
271
|
+
|
|
272
|
+
Attributes:
|
|
273
|
+
root_task: The root task.
|
|
274
|
+
tasks: The ordered tasks.
|
|
275
|
+
task_map: A map for task.id to Task.
|
|
276
|
+
current_task_id: The current "RUNNING" task.id.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
task (Task): The root Task.
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
def __init__(self, task: Task):
|
|
283
|
+
self.root_task: Task = task
|
|
284
|
+
self.current_task_id: str = task.id
|
|
285
|
+
self.tasks: List[Task] = [task]
|
|
286
|
+
self.task_map: Dict[str, Task] = {task.id: task}
|
|
287
|
+
|
|
288
|
+
def gen_task_id(self) -> str:
|
|
289
|
+
r"""Generate a new task id."""
|
|
290
|
+
return f"{len(self.tasks)}"
|
|
291
|
+
|
|
292
|
+
def exist(self, task_id: str) -> bool:
|
|
293
|
+
r"""Check if a task with the given id exists."""
|
|
294
|
+
return task_id in self.task_map
|
|
295
|
+
|
|
296
|
+
@property
|
|
297
|
+
def current_task(self) -> Optional[Task]:
|
|
298
|
+
r"""Get the current task."""
|
|
299
|
+
return self.task_map.get(self.current_task_id, None)
|
|
300
|
+
|
|
301
|
+
@staticmethod
|
|
302
|
+
def topological_sort(tasks: List[Task]) -> List[Task]:
|
|
303
|
+
r"""Sort a list of tasks by topological way.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
tasks (List[Task]): The giving list of tasks.
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
The sorted list of tasks.
|
|
310
|
+
"""
|
|
311
|
+
stack = []
|
|
312
|
+
visited = set()
|
|
313
|
+
|
|
314
|
+
# recursive visit the vertices
|
|
315
|
+
def visit(task: Task):
|
|
316
|
+
if task.id in visited:
|
|
317
|
+
return
|
|
318
|
+
visited.add(task.id)
|
|
319
|
+
|
|
320
|
+
# go deep for dependencies
|
|
321
|
+
for sub_task in task.subtasks:
|
|
322
|
+
visit(sub_task)
|
|
323
|
+
|
|
324
|
+
# add current task to stack which have no dependencies.
|
|
325
|
+
stack.append(task)
|
|
326
|
+
|
|
327
|
+
for task in tasks:
|
|
328
|
+
visit(task)
|
|
329
|
+
|
|
330
|
+
return stack
|
|
331
|
+
|
|
332
|
+
@staticmethod
|
|
333
|
+
def set_tasks_dependence(
|
|
334
|
+
root: Task,
|
|
335
|
+
others: List[Task],
|
|
336
|
+
type: Literal["serial", "parallel"] = "parallel",
|
|
337
|
+
):
|
|
338
|
+
r"""Set relationship between root task and other tasks.
|
|
339
|
+
Two relationships are currently supported: serial and parallel.
|
|
340
|
+
`serial` : root -> other1 -> other2
|
|
341
|
+
`parallel`: root -> other1
|
|
342
|
+
-> other2
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
root (Task): A root task.
|
|
346
|
+
others (List[Task]): A list of tasks.
|
|
347
|
+
"""
|
|
348
|
+
# filter the root task in the others to avoid self-loop dependence.
|
|
349
|
+
others = [other for other in others if other != root]
|
|
350
|
+
|
|
351
|
+
if len(others) == 0:
|
|
352
|
+
return
|
|
353
|
+
if type == "parallel":
|
|
354
|
+
for other in others:
|
|
355
|
+
root.add_subtask(other)
|
|
356
|
+
else:
|
|
357
|
+
parent = root
|
|
358
|
+
for child in others:
|
|
359
|
+
parent.add_subtask(child)
|
|
360
|
+
parent = child
|
|
361
|
+
|
|
362
|
+
def add_tasks(self, tasks: Union[Task, List[Task]]) -> None:
|
|
363
|
+
r"""self.tasks and self.task_map will be updated by the input tasks."""
|
|
364
|
+
if not tasks:
|
|
365
|
+
return
|
|
366
|
+
if not isinstance(tasks, List):
|
|
367
|
+
tasks = [tasks]
|
|
368
|
+
for task in tasks:
|
|
369
|
+
assert not self.exist(task.id), f"`{task.id}` already existed."
|
|
370
|
+
self.tasks = self.topological_sort(self.tasks + tasks)
|
|
371
|
+
self.task_map = {task.id: task for task in self.tasks}
|
|
372
|
+
|
|
373
|
+
def evolve(
|
|
374
|
+
self,
|
|
375
|
+
task: Task,
|
|
376
|
+
agent: ChatAgent,
|
|
377
|
+
template: Optional[TextPrompt] = None,
|
|
378
|
+
task_parser: Optional[Callable[[str, str], List[Task]]] = None,
|
|
379
|
+
) -> Optional[Task]:
|
|
380
|
+
r"""Evolve a task to a new task.
|
|
381
|
+
Evolve is only used for data generation.
|
|
382
|
+
Args:
|
|
383
|
+
task (Task): A given task.
|
|
384
|
+
agent (ChatAgent): An agent that used to evolve the task.
|
|
385
|
+
template (TextPrompt, optional): A prompt template to evolve task.
|
|
386
|
+
If not provided, the default template will be used.
|
|
387
|
+
task_parser (Callable, optional): A function to extract Task from
|
|
388
|
+
response. If not provided, the default parser will be used.
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
Task: The created :obj:`Task` instance or None.
|
|
392
|
+
"""
|
|
393
|
+
|
|
394
|
+
if template is None:
|
|
395
|
+
template = TASK_EVOLVE_PROMPT
|
|
396
|
+
|
|
397
|
+
role_name = agent.role_name
|
|
398
|
+
content = template.format(role_name=role_name, content=task.content)
|
|
399
|
+
msg = BaseMessage.make_user_message(
|
|
400
|
+
role_name=role_name, content=content
|
|
401
|
+
)
|
|
402
|
+
response = agent.step(msg)
|
|
403
|
+
if task_parser is None:
|
|
404
|
+
task_parser = parse_response
|
|
405
|
+
tasks = task_parser(response.msg.content, task.id)
|
|
406
|
+
if tasks:
|
|
407
|
+
return tasks[0]
|
|
408
|
+
return None
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
TASK_DECOMPOSE_PROMPT = TextPrompt(
|
|
18
|
+
"""As a Task Decomposer with the role of {role_name}, your objective is to divide the given task into subtasks.
|
|
19
|
+
You have been provided with the following objective:
|
|
20
|
+
|
|
21
|
+
{content}
|
|
22
|
+
|
|
23
|
+
Please format the subtasks as a numbered list within <tasks> tags, as demonstrated below:
|
|
24
|
+
<tasks>
|
|
25
|
+
<task>Subtask 1</task>
|
|
26
|
+
<task>Subtask 2</task>
|
|
27
|
+
</tasks>
|
|
28
|
+
|
|
29
|
+
Each subtask should be concise, concrete, and achievable for a {role_name}.
|
|
30
|
+
Ensure that the task plan is created without asking any questions.
|
|
31
|
+
Be specific and clear.
|
|
32
|
+
"""
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
TASK_COMPOSE_PROMPT = TextPrompt(
|
|
37
|
+
"""As a Task composer with the role of {role_name}, your objective is to gather result from all sub tasks to get the final answer.
|
|
38
|
+
The root task is:
|
|
39
|
+
|
|
40
|
+
{content}
|
|
41
|
+
|
|
42
|
+
The related tasks result and status:
|
|
43
|
+
|
|
44
|
+
{other_results}
|
|
45
|
+
|
|
46
|
+
so, the final answer of the root task is:
|
|
47
|
+
"""
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
TASK_EVOLVE_PROMPT = TextPrompt(
|
|
52
|
+
"""As a Task Creator for {role_name}, your objective is to draw inspiration from the provided task to develop an entirely new one.
|
|
53
|
+
The new task should fall within the same domain as the given task but be more complex and unique.
|
|
54
|
+
It must be reasonable, understandable, and actionable by {role_name}.
|
|
55
|
+
The created task must be enclosed within <task> </task> tags.
|
|
56
|
+
<task>
|
|
57
|
+
... created task
|
|
58
|
+
</task>
|
|
59
|
+
|
|
60
|
+
## given task
|
|
61
|
+
{content}
|
|
62
|
+
|
|
63
|
+
## created task
|
|
64
|
+
"""
|
|
65
|
+
)
|
camel/toolkits/__init__.py
CHANGED
|
@@ -27,6 +27,7 @@ from .search_toolkit import SEARCH_FUNCS, SearchToolkit
|
|
|
27
27
|
from .twitter_toolkit import TWITTER_FUNCS, TwitterToolkit
|
|
28
28
|
from .weather_toolkit import WEATHER_FUNCS, WeatherToolkit
|
|
29
29
|
from .slack_toolkit import SLACK_FUNCS, SlackToolkit
|
|
30
|
+
from .dalle_toolkit import DALLE_FUNCS, DalleToolkit
|
|
30
31
|
|
|
31
32
|
from .base import BaseToolkit
|
|
32
33
|
from .code_execution import CodeExecutionToolkit
|
|
@@ -45,12 +46,14 @@ __all__ = [
|
|
|
45
46
|
'TWITTER_FUNCS',
|
|
46
47
|
'WEATHER_FUNCS',
|
|
47
48
|
'SLACK_FUNCS',
|
|
49
|
+
'DALLE_FUNCS',
|
|
48
50
|
'BaseToolkit',
|
|
49
51
|
'GithubToolkit',
|
|
50
52
|
'MathToolkit',
|
|
51
53
|
'GoogleMapsToolkit',
|
|
52
54
|
'SearchToolkit',
|
|
53
55
|
'SlackToolkit',
|
|
56
|
+
'DalleToolkit',
|
|
54
57
|
'TwitterToolkit',
|
|
55
58
|
'WeatherToolkit',
|
|
56
59
|
'RetrievalToolkit',
|
camel/toolkits/base.py
CHANGED
|
@@ -14,9 +14,11 @@
|
|
|
14
14
|
|
|
15
15
|
from typing import List
|
|
16
16
|
|
|
17
|
+
from camel.utils import AgentOpsMeta
|
|
18
|
+
|
|
17
19
|
from .openai_function import OpenAIFunction
|
|
18
20
|
|
|
19
21
|
|
|
20
|
-
class BaseToolkit:
|
|
22
|
+
class BaseToolkit(metaclass=AgentOpsMeta):
|
|
21
23
|
def get_tools(self) -> List[OpenAIFunction]:
|
|
22
24
|
raise NotImplementedError("Subclasses must implement this method.")
|