camel-ai 0.2.11__py3-none-any.whl → 0.2.12__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 +13 -1
- camel/benchmarks/__init__.py +18 -0
- camel/benchmarks/base.py +152 -0
- camel/benchmarks/gaia.py +478 -0
- camel/configs/__init__.py +3 -0
- camel/configs/ollama_config.py +4 -2
- camel/configs/sglang_config.py +71 -0
- camel/data_collector/__init__.py +19 -0
- camel/data_collector/alpaca_collector.py +127 -0
- camel/data_collector/base.py +211 -0
- camel/data_collector/sharegpt_collector.py +205 -0
- camel/datahubs/__init__.py +23 -0
- camel/datahubs/base.py +136 -0
- camel/datahubs/huggingface.py +433 -0
- camel/datahubs/models.py +22 -0
- camel/interpreters/__init__.py +2 -0
- camel/interpreters/e2b_interpreter.py +136 -0
- camel/loaders/__init__.py +3 -1
- camel/loaders/base_io.py +41 -41
- camel/messages/__init__.py +2 -0
- camel/models/__init__.py +2 -0
- camel/models/anthropic_model.py +14 -4
- camel/models/base_model.py +28 -0
- camel/models/groq_model.py +1 -1
- camel/models/model_factory.py +3 -0
- camel/models/ollama_model.py +12 -0
- camel/models/openai_model.py +0 -26
- camel/models/reward/__init__.py +22 -0
- camel/models/reward/base_reward_model.py +58 -0
- camel/models/reward/evaluator.py +63 -0
- camel/models/reward/nemotron_model.py +112 -0
- camel/models/sglang_model.py +225 -0
- camel/models/vllm_model.py +1 -1
- camel/personas/persona_hub.py +2 -2
- camel/schemas/openai_converter.py +2 -2
- camel/societies/workforce/role_playing_worker.py +2 -2
- camel/societies/workforce/single_agent_worker.py +2 -2
- camel/societies/workforce/workforce.py +3 -3
- camel/storages/object_storages/amazon_s3.py +2 -2
- camel/storages/object_storages/azure_blob.py +2 -2
- camel/storages/object_storages/google_cloud.py +2 -2
- camel/toolkits/__init__.py +2 -0
- camel/toolkits/code_execution.py +5 -1
- camel/toolkits/function_tool.py +41 -0
- camel/toolkits/math_toolkit.py +47 -16
- camel/toolkits/search_toolkit.py +154 -2
- camel/toolkits/stripe_toolkit.py +273 -0
- camel/types/__init__.py +2 -0
- camel/types/enums.py +27 -2
- camel/utils/token_counting.py +22 -10
- {camel_ai-0.2.11.dist-info → camel_ai-0.2.12.dist-info}/METADATA +13 -6
- {camel_ai-0.2.11.dist-info → camel_ai-0.2.12.dist-info}/RECORD +55 -36
- {camel_ai-0.2.11.dist-info → camel_ai-0.2.12.dist-info}/LICENSE +0 -0
- {camel_ai-0.2.11.dist-info → camel_ai-0.2.12.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
|
|
15
|
+
from typing import Any, Dict, List, Optional, Union
|
|
16
|
+
|
|
17
|
+
from typing_extensions import Self
|
|
18
|
+
|
|
19
|
+
from camel.agents import ChatAgent
|
|
20
|
+
from camel.data_collector.base import BaseDataCollector
|
|
21
|
+
from camel.messages import AlpacaItem, BaseMessage
|
|
22
|
+
from camel.schemas import OpenAISchemaConverter
|
|
23
|
+
|
|
24
|
+
# ruff: noqa: E501
|
|
25
|
+
DEFAULT_CONVERTER_PROMPTS = """
|
|
26
|
+
Extract key entities and attributes from the conversations
|
|
27
|
+
and convert them into a structured JSON format.
|
|
28
|
+
For example:
|
|
29
|
+
Instruction: You are a helpful assistant.
|
|
30
|
+
User: When is the release date of the video game Portal?
|
|
31
|
+
Assistant: The release date of the video game Portal is October 9.
|
|
32
|
+
Your output should be:
|
|
33
|
+
{
|
|
34
|
+
"instruction": "You are a helpful assistant. When is the release date of the video game Portal?",
|
|
35
|
+
"input": "",
|
|
36
|
+
"output": "The release date of the video game Portal is October 9."
|
|
37
|
+
}
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AlpacaDataCollector(BaseDataCollector):
|
|
42
|
+
def __init__(self) -> None:
|
|
43
|
+
super().__init__()
|
|
44
|
+
self.system_message: Optional[BaseMessage] = None
|
|
45
|
+
self.agent_name: Optional[str] = None
|
|
46
|
+
|
|
47
|
+
def record(
|
|
48
|
+
self,
|
|
49
|
+
agent: Union[List[ChatAgent], ChatAgent],
|
|
50
|
+
) -> Self:
|
|
51
|
+
r"""Inject an agent into the data collector.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
agent (Union[List[ChatAgent], ChatAgent]):
|
|
55
|
+
The agent to inject.
|
|
56
|
+
"""
|
|
57
|
+
if not self.agent_name:
|
|
58
|
+
_agent = agent if isinstance(agent, ChatAgent) else agent[0]
|
|
59
|
+
self.agent_name = _agent.role_name
|
|
60
|
+
self.system_message = _agent._system_message
|
|
61
|
+
super().record(agent)
|
|
62
|
+
return self
|
|
63
|
+
|
|
64
|
+
def convert(self) -> Dict[str, Any]:
|
|
65
|
+
r"""Convert the collected data into a dictionary."""
|
|
66
|
+
if self.agent_name is None:
|
|
67
|
+
raise ValueError("No agent injected")
|
|
68
|
+
|
|
69
|
+
history = self.get_agent_history(self.agent_name)
|
|
70
|
+
if not history:
|
|
71
|
+
raise ValueError("No data collected.")
|
|
72
|
+
|
|
73
|
+
# Validate and process history
|
|
74
|
+
if len(history) == 3 and history[0].role == "system":
|
|
75
|
+
history = history[1:] # Ignore the system message.
|
|
76
|
+
elif len(history) != 2:
|
|
77
|
+
raise ValueError(
|
|
78
|
+
f"AlpacaDataCollector only supports one message pair, but "
|
|
79
|
+
f"got {len(history)}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
input_message, output_message = history
|
|
83
|
+
instruction = (
|
|
84
|
+
self.system_message.content if self.system_message else ""
|
|
85
|
+
) + str(input_message.message)
|
|
86
|
+
|
|
87
|
+
data = {
|
|
88
|
+
"instruction": instruction,
|
|
89
|
+
"input": "",
|
|
90
|
+
"output": output_message.message,
|
|
91
|
+
}
|
|
92
|
+
self.data.append(data)
|
|
93
|
+
return data
|
|
94
|
+
|
|
95
|
+
def llm_convert(
|
|
96
|
+
self,
|
|
97
|
+
converter: Optional[OpenAISchemaConverter] = None,
|
|
98
|
+
prompt: Optional[str] = None,
|
|
99
|
+
) -> Dict[str, str]:
|
|
100
|
+
r"""Convert collected data using an LLM schema converter.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
converter (Optional[OpenAISchemaConverter], optional):
|
|
104
|
+
The converter to use. (default: :obj:`OpenAISchemaConverter`)
|
|
105
|
+
prompt (Optional[str], optional): Prompt to guide the conversion.
|
|
106
|
+
(default: :obj:`DEFAULT_CONVERTER_PROMPTS`)
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Dict[str, str]: The converted data.
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
ValueError: If no agent is injected or data cannot be collected.
|
|
113
|
+
"""
|
|
114
|
+
prompt = prompt or DEFAULT_CONVERTER_PROMPTS
|
|
115
|
+
converter = converter or OpenAISchemaConverter()
|
|
116
|
+
|
|
117
|
+
system = self.system_message.content if self.system_message else ""
|
|
118
|
+
context = [f"Instruction: {system}\n"]
|
|
119
|
+
|
|
120
|
+
for message in self.get_agent_history(str(self.agent_name)):
|
|
121
|
+
if message.role == "user":
|
|
122
|
+
context.append(f"User: {message.message}\n")
|
|
123
|
+
else:
|
|
124
|
+
context.append(f"{message.name}: {message.message}\n")
|
|
125
|
+
return converter.convert(
|
|
126
|
+
"\n".join(context), AlpacaItem, prompt=prompt
|
|
127
|
+
).model_dump()
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
|
|
15
|
+
import uuid
|
|
16
|
+
from abc import ABC, abstractmethod
|
|
17
|
+
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
|
|
18
|
+
from uuid import UUID
|
|
19
|
+
|
|
20
|
+
from typing_extensions import Self
|
|
21
|
+
|
|
22
|
+
from camel.agents import ChatAgent
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CollectorData:
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
id: UUID,
|
|
29
|
+
name: str,
|
|
30
|
+
role: Literal["user", "assistant", "system", "function"],
|
|
31
|
+
message: Optional[str] = None,
|
|
32
|
+
function_call: Optional[Dict[str, Any]] = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
r"""Create a data item store information about a message.
|
|
35
|
+
Used by the data collector.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
|
|
39
|
+
id (UUID): The id of the message.
|
|
40
|
+
name (str): The name of the agent.
|
|
41
|
+
role (Literal["user", "assistant", "system", "function"]):
|
|
42
|
+
The role of the message.
|
|
43
|
+
message (Optional[str], optional): The message.
|
|
44
|
+
(default: :obj:`None`)
|
|
45
|
+
function_call (Optional[Dict[str, Any]], optional):
|
|
46
|
+
The function call. (default: :obj:`None`)
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
|
|
50
|
+
ValueError: If the role is not supported.
|
|
51
|
+
ValueError: If the role is system and function call is provided.
|
|
52
|
+
ValueError: If neither message nor function call is provided.
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
if role not in ["user", "assistant", "system", "function"]:
|
|
56
|
+
raise ValueError(f"Role {role} not supported")
|
|
57
|
+
if role == "system" and function_call:
|
|
58
|
+
raise ValueError("System role cannot have function call")
|
|
59
|
+
if not message and not function_call:
|
|
60
|
+
raise ValueError(
|
|
61
|
+
"Either message or function call must be provided"
|
|
62
|
+
)
|
|
63
|
+
self.id = id
|
|
64
|
+
self.name = name
|
|
65
|
+
self.role = role
|
|
66
|
+
self.message = message
|
|
67
|
+
self.function_call = function_call
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def from_context(name, context: Dict[str, Any]) -> "CollectorData":
|
|
71
|
+
r"""Create a data collector from a context.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
name (str): The name of the agent.
|
|
75
|
+
context (Dict[str, Any]): The context.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
CollectorData: The data collector.
|
|
79
|
+
"""
|
|
80
|
+
return CollectorData(
|
|
81
|
+
id=uuid.uuid4(),
|
|
82
|
+
name=name,
|
|
83
|
+
role=context["role"],
|
|
84
|
+
message=context["content"],
|
|
85
|
+
function_call=context.get("function_call", None),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class BaseDataCollector(ABC):
|
|
90
|
+
r"""Base class for data collectors."""
|
|
91
|
+
|
|
92
|
+
def __init__(self) -> None:
|
|
93
|
+
r"""Create a data collector."""
|
|
94
|
+
self.history: List[CollectorData] = []
|
|
95
|
+
self._recording = False
|
|
96
|
+
self.agents: List[Tuple[str, ChatAgent]] = []
|
|
97
|
+
self.data: List[Dict[str, Any]] = []
|
|
98
|
+
|
|
99
|
+
def step(
|
|
100
|
+
self,
|
|
101
|
+
role: Literal["user", "assistant", "system", "function"],
|
|
102
|
+
name: Optional[str] = None,
|
|
103
|
+
message: Optional[str] = None,
|
|
104
|
+
function_call: Optional[Dict[str, Any]] = None,
|
|
105
|
+
) -> Self:
|
|
106
|
+
r"""Record a message.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
role (Literal["user", "assistant", "system", "function"]):
|
|
110
|
+
The role of the message.
|
|
111
|
+
name (Optional[str], optional): The name of the agent.
|
|
112
|
+
(default: :obj:`None`)
|
|
113
|
+
message (Optional[str], optional): The message to record.
|
|
114
|
+
(default: :obj:`None`)
|
|
115
|
+
function_call (Optional[Dict[str, Any]], optional):
|
|
116
|
+
The function call to record. (default: :obj:`None`)
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Self: The data collector.
|
|
120
|
+
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
name = name or role
|
|
124
|
+
|
|
125
|
+
self.history.append(
|
|
126
|
+
CollectorData(
|
|
127
|
+
id=uuid.uuid4(),
|
|
128
|
+
name=name,
|
|
129
|
+
role=role,
|
|
130
|
+
message=message,
|
|
131
|
+
function_call=function_call,
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
return self
|
|
135
|
+
|
|
136
|
+
def record(
|
|
137
|
+
self,
|
|
138
|
+
agent: Union[List[ChatAgent], ChatAgent],
|
|
139
|
+
) -> Self:
|
|
140
|
+
r"""Record agents.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
agent (Union[List[ChatAgent], ChatAgent]):
|
|
144
|
+
The agent(s) to inject.
|
|
145
|
+
"""
|
|
146
|
+
if not isinstance(agent, list):
|
|
147
|
+
agent = [agent]
|
|
148
|
+
for a in agent:
|
|
149
|
+
name = a.role_name
|
|
150
|
+
if not name:
|
|
151
|
+
name = f"{a.__class__.__name__}_{len(self.agents)}"
|
|
152
|
+
if name in [n for n, _ in self.agents]:
|
|
153
|
+
raise ValueError(f"Name {name} already exists")
|
|
154
|
+
|
|
155
|
+
self.agents.append((name, a))
|
|
156
|
+
return self
|
|
157
|
+
|
|
158
|
+
def start(self) -> Self:
|
|
159
|
+
r"""Start recording."""
|
|
160
|
+
self._recording = True
|
|
161
|
+
return self
|
|
162
|
+
|
|
163
|
+
def stop(self) -> Self:
|
|
164
|
+
r"""Stop recording."""
|
|
165
|
+
self._recording = False
|
|
166
|
+
return self
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def recording(self) -> bool:
|
|
170
|
+
r"""Whether the collector is recording."""
|
|
171
|
+
return self._recording
|
|
172
|
+
|
|
173
|
+
def reset(self, reset_agents: bool = True):
|
|
174
|
+
r"""Reset the collector.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
reset_agents (bool, optional):
|
|
178
|
+
Whether to reset the agents. Defaults to True.
|
|
179
|
+
"""
|
|
180
|
+
self.history = []
|
|
181
|
+
if reset_agents:
|
|
182
|
+
for _, agent in self.agents:
|
|
183
|
+
agent.reset()
|
|
184
|
+
|
|
185
|
+
@abstractmethod
|
|
186
|
+
def convert(self) -> Any:
|
|
187
|
+
r"""Convert the collected data."""
|
|
188
|
+
pass
|
|
189
|
+
|
|
190
|
+
@abstractmethod
|
|
191
|
+
def llm_convert(self, converter: Any, prompt: Optional[str] = None) -> Any:
|
|
192
|
+
r"""Convert the collected data."""
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
def get_agent_history(self, name: str) -> List[CollectorData]:
|
|
196
|
+
r"""Get the message history of an agent.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
name (str): The name of the agent.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
List[CollectorData]: The message history of the agent
|
|
203
|
+
"""
|
|
204
|
+
if not self.history:
|
|
205
|
+
for _name, agent in self.agents:
|
|
206
|
+
if _name == name:
|
|
207
|
+
return [
|
|
208
|
+
CollectorData.from_context(name, dict(i))
|
|
209
|
+
for i in agent.memory.get_context()[0]
|
|
210
|
+
]
|
|
211
|
+
return [msg for msg in self.history if msg.name == name]
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
from typing import Any, ClassVar, Dict, List, Literal, Optional, Union
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel
|
|
19
|
+
from typing_extensions import Self
|
|
20
|
+
|
|
21
|
+
from camel.agents import ChatAgent
|
|
22
|
+
from camel.data_collector.base import BaseDataCollector
|
|
23
|
+
from camel.messages import BaseMessage
|
|
24
|
+
from camel.messages.conversion.conversation_models import (
|
|
25
|
+
ShareGPTConversation,
|
|
26
|
+
ShareGPTMessage,
|
|
27
|
+
)
|
|
28
|
+
from camel.schemas import OpenAISchemaConverter
|
|
29
|
+
from camel.toolkits import FunctionTool
|
|
30
|
+
|
|
31
|
+
FROM_HASH = {
|
|
32
|
+
"human": "human",
|
|
33
|
+
"gpt": "gpt",
|
|
34
|
+
"observation": "human",
|
|
35
|
+
"function_call": "gpt",
|
|
36
|
+
}
|
|
37
|
+
# ruff: noqa: E501
|
|
38
|
+
DEFAULT_CONVERTER_PROMPTS = """
|
|
39
|
+
Extract key entities and attributes from the conversations
|
|
40
|
+
and convert them into a structured JSON format.
|
|
41
|
+
For example:
|
|
42
|
+
System: You are a helpful assistant
|
|
43
|
+
Tools: [{"name": "get_release_date", "arguments": ["Portal"]}]
|
|
44
|
+
User: When is the release date of the video game Portal?
|
|
45
|
+
Assistant: The release date of the video game Portal is October 9, 2007.
|
|
46
|
+
Your output should be:
|
|
47
|
+
{
|
|
48
|
+
"system": "You are a helpful assistant",
|
|
49
|
+
"tools": "[{"name": "get_release_date", "arguments": ["Portal"]}]",
|
|
50
|
+
"conversations": [
|
|
51
|
+
{"from": "human", "value": "When is the release date of the video game Portal?"},
|
|
52
|
+
{"from": "gpt", "value": "The release date of the video game Portal is October 9, 2007."}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ConversationItem(BaseModel):
|
|
59
|
+
from_: Literal["human", "gpt", "function_call", "observation"]
|
|
60
|
+
value: str
|
|
61
|
+
|
|
62
|
+
class Config:
|
|
63
|
+
fields: ClassVar[Dict[str, str]] = {"from_": "from"}
|
|
64
|
+
extra = "forbid"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class ShareGPTData(BaseModel):
|
|
68
|
+
system: str
|
|
69
|
+
tools: str
|
|
70
|
+
conversations: List[ConversationItem]
|
|
71
|
+
|
|
72
|
+
class Config:
|
|
73
|
+
extra = "forbid"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ShareGPTDataCollector(BaseDataCollector):
|
|
77
|
+
def __init__(self) -> None:
|
|
78
|
+
super().__init__()
|
|
79
|
+
self.system_message: Optional[BaseMessage] = None
|
|
80
|
+
self.agent_name: Optional[str] = None
|
|
81
|
+
self.tools: List[FunctionTool] = []
|
|
82
|
+
|
|
83
|
+
def record(
|
|
84
|
+
self,
|
|
85
|
+
agent: Union[List[ChatAgent], ChatAgent],
|
|
86
|
+
) -> Self:
|
|
87
|
+
r"""Inject an agent into the data collector."""
|
|
88
|
+
if not self.agent_name:
|
|
89
|
+
_agent = agent if isinstance(agent, ChatAgent) else agent[0]
|
|
90
|
+
self.agent_name = _agent.role_name
|
|
91
|
+
self.system_message = _agent._system_message
|
|
92
|
+
self.tools += list(_agent.tool_dict.values())
|
|
93
|
+
|
|
94
|
+
super().record(agent)
|
|
95
|
+
return self
|
|
96
|
+
|
|
97
|
+
def convert(self) -> Dict[str, Any]:
|
|
98
|
+
r"""Convert the collected data into a dictionary."""
|
|
99
|
+
if self.agent_name is None:
|
|
100
|
+
raise ValueError("No agent injected")
|
|
101
|
+
|
|
102
|
+
history = self.get_agent_history(self.agent_name)
|
|
103
|
+
if not history:
|
|
104
|
+
raise ValueError("No data collected.")
|
|
105
|
+
|
|
106
|
+
data = dict(
|
|
107
|
+
system=self.system_message.content if self.system_message else "",
|
|
108
|
+
tools=json.dumps(
|
|
109
|
+
[t.get_openai_tool_schema()["function"] for t in self.tools]
|
|
110
|
+
),
|
|
111
|
+
conversations=[],
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
conversations: List[Any] = []
|
|
115
|
+
for _data in history:
|
|
116
|
+
role, message = _data.role, _data
|
|
117
|
+
|
|
118
|
+
if role == "user":
|
|
119
|
+
conversations.append(
|
|
120
|
+
{"from": "human", "value": message.message}
|
|
121
|
+
)
|
|
122
|
+
elif role == "assistant":
|
|
123
|
+
if message.function_call:
|
|
124
|
+
conversations.append(
|
|
125
|
+
{
|
|
126
|
+
"from": "function_call",
|
|
127
|
+
"value": json.dumps(message.function_call),
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
else:
|
|
131
|
+
conversations.append(
|
|
132
|
+
{"from": "gpt", "value": message.message}
|
|
133
|
+
)
|
|
134
|
+
elif role == "function":
|
|
135
|
+
conversations.append(
|
|
136
|
+
{
|
|
137
|
+
"from": "observation",
|
|
138
|
+
"value": json.dumps(message.message), # type: ignore[attr-defined]
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
data["conversations"] = conversations
|
|
142
|
+
|
|
143
|
+
self.data.append(data)
|
|
144
|
+
return data
|
|
145
|
+
|
|
146
|
+
def llm_convert(
|
|
147
|
+
self,
|
|
148
|
+
converter: Optional[OpenAISchemaConverter] = None,
|
|
149
|
+
prompt: Optional[str] = None,
|
|
150
|
+
) -> Dict[str, Any]:
|
|
151
|
+
r"""Convert collected data using an LLM schema converter.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
converter (Optional[OpenAISchemaConverter], optional):
|
|
155
|
+
The converter to use. (default: :obj:`OpenAISchemaConverter`)
|
|
156
|
+
prompt (Optional[str], optional): Prompt to guide the conversion.
|
|
157
|
+
(default: :obj:`DEFAULT_CONVERTER_PROMPTS`)
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Dict[str, str]: The converted data.
|
|
161
|
+
|
|
162
|
+
Raises:
|
|
163
|
+
ValueError: If no agent is injected or data cannot be collected.
|
|
164
|
+
"""
|
|
165
|
+
prompt = prompt or DEFAULT_CONVERTER_PROMPTS
|
|
166
|
+
converter = converter or OpenAISchemaConverter()
|
|
167
|
+
|
|
168
|
+
system = self.system_message.content if self.system_message else ""
|
|
169
|
+
context = [f"System: {system}\n"]
|
|
170
|
+
|
|
171
|
+
context.append(
|
|
172
|
+
"Tools: "
|
|
173
|
+
+ json.dumps(
|
|
174
|
+
[t.get_openai_tool_schema()["function"] for t in self.tools]
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
for _data in self.get_agent_history(str(self.agent_name)):
|
|
178
|
+
role, message = _data.role, _data
|
|
179
|
+
prefix = (
|
|
180
|
+
f"{role}: " if role != "user" else "User: " + f"{_data.name}: "
|
|
181
|
+
)
|
|
182
|
+
if message.function_call:
|
|
183
|
+
context.append(prefix + json.dumps(message.function_call))
|
|
184
|
+
|
|
185
|
+
elif role == "function":
|
|
186
|
+
context.append(prefix + json.dumps(message.message)) # type: ignore[attr-defined]
|
|
187
|
+
else:
|
|
188
|
+
context.append(prefix + str(message.message))
|
|
189
|
+
return converter.convert(
|
|
190
|
+
"\n".join(context), ShareGPTData, prompt
|
|
191
|
+
).model_dump()
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def to_sharegpt_conversation(data: Dict[str, Any]) -> ShareGPTConversation:
|
|
195
|
+
messages = [
|
|
196
|
+
ShareGPTMessage(from_="system", value=data["system"]) # type: ignore[call-arg]
|
|
197
|
+
]
|
|
198
|
+
for item in data["conversations"]:
|
|
199
|
+
messages.append(
|
|
200
|
+
ShareGPTMessage( # type: ignore[call-arg]
|
|
201
|
+
from_=FROM_HASH[item["from"]],
|
|
202
|
+
value=item["value"],
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
return ShareGPTConversation(root=messages)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
|
|
15
|
+
from .base import BaseDatasetManager
|
|
16
|
+
from .huggingface import HuggingFaceDatasetManager
|
|
17
|
+
from .models import Record
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"BaseDatasetManager",
|
|
21
|
+
"Record",
|
|
22
|
+
"HuggingFaceDatasetManager",
|
|
23
|
+
]
|
camel/datahubs/base.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
from abc import ABC, abstractmethod
|
|
15
|
+
from typing import Any, List
|
|
16
|
+
|
|
17
|
+
from camel.datahubs.models import Record
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseDatasetManager(ABC):
|
|
21
|
+
r"""Abstract base class for dataset managers."""
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def create_dataset(self, name: str, **kwargs: Any) -> str:
|
|
25
|
+
r"""Creates a new dataset.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
name (str): The name of the dataset.
|
|
29
|
+
kwargs (Any): Additional keyword arguments.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
str: The URL of the created dataset.
|
|
33
|
+
"""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def list_datasets(
|
|
38
|
+
self, username: str, limit: int = 100, **kwargs: Any
|
|
39
|
+
) -> List[str]:
|
|
40
|
+
r"""Lists all datasets for the current user.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
username (str): The username of the user whose datasets to list.
|
|
44
|
+
limit (int): The maximum number of datasets to list.
|
|
45
|
+
(default::obj:`100`)
|
|
46
|
+
kwargs (Any): Additional keyword arguments.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
List[str]: A list of dataset ids.
|
|
50
|
+
"""
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def delete_dataset(self, dataset_name: str, **kwargs: Any) -> None:
|
|
55
|
+
r"""Deletes a dataset.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
dataset_name (str): The name of the dataset to delete.
|
|
59
|
+
kwargs (Any): Additional keyword arguments.
|
|
60
|
+
"""
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def add_records(
|
|
65
|
+
self,
|
|
66
|
+
dataset_name: str,
|
|
67
|
+
records: List[Record],
|
|
68
|
+
filepath: str = "records/records.json",
|
|
69
|
+
**kwargs: Any,
|
|
70
|
+
) -> None:
|
|
71
|
+
r"""Adds records to a dataset.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
dataset_name (str): The name of the dataset.
|
|
75
|
+
records (List[Record]): A list of records to add to the dataset.
|
|
76
|
+
filepath (str): The path to the file containing the records.
|
|
77
|
+
(default::obj:`"records/records.json"`)
|
|
78
|
+
kwargs (Any): Additional keyword arguments.
|
|
79
|
+
"""
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
def update_records(
|
|
84
|
+
self,
|
|
85
|
+
dataset_name: str,
|
|
86
|
+
records: List[Record],
|
|
87
|
+
filepath: str = "records/records.json",
|
|
88
|
+
**kwargs: Any,
|
|
89
|
+
) -> None:
|
|
90
|
+
r"""Updates records in a dataset.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
dataset_name (str): The name of the dataset.
|
|
94
|
+
records (List[Record]): A list of records to update in the dataset.
|
|
95
|
+
filepath (str): The path to the file containing the records.
|
|
96
|
+
(default::obj:`"records/records.json"`)
|
|
97
|
+
kwargs (Any): Additional keyword arguments.
|
|
98
|
+
"""
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
@abstractmethod
|
|
102
|
+
def list_records(
|
|
103
|
+
self,
|
|
104
|
+
dataset_name: str,
|
|
105
|
+
filepath: str = "records/records.json",
|
|
106
|
+
**kwargs: Any,
|
|
107
|
+
) -> List[Record]:
|
|
108
|
+
r"""Lists records in a dataset.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
dataset_name (str): The name of the dataset.
|
|
112
|
+
filepath (str): The path to the file containing the records.
|
|
113
|
+
(default::obj:`"records/records.json"`)
|
|
114
|
+
kwargs (Any): Additional keyword arguments.
|
|
115
|
+
"""
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
# New method for record deletion
|
|
119
|
+
@abstractmethod
|
|
120
|
+
def delete_record(
|
|
121
|
+
self,
|
|
122
|
+
dataset_name: str,
|
|
123
|
+
record_id: str,
|
|
124
|
+
filepath: str = "records/records.json",
|
|
125
|
+
**kwargs: Any,
|
|
126
|
+
) -> None:
|
|
127
|
+
r"""Deletes a record from the dataset.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
dataset_name (str): The name of the dataset.
|
|
131
|
+
record_id (str): The ID of the record to delete.
|
|
132
|
+
filepath (str): The path to the file containing the records.
|
|
133
|
+
(default::obj:`"records/records.json"`)
|
|
134
|
+
kwargs (Any): Additional keyword arguments.
|
|
135
|
+
"""
|
|
136
|
+
pass
|