camel-ai 0.2.51__py3-none-any.whl → 0.2.53__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/__init__.py +2 -0
- camel/agents/chat_agent.py +20 -0
- camel/agents/mcp_agent.py +233 -0
- camel/embeddings/__init__.py +2 -0
- camel/embeddings/gemini_embedding.py +115 -0
- camel/embeddings/openai_embedding.py +11 -11
- camel/embeddings/together_embedding.py +7 -7
- camel/models/model_factory.py +6 -0
- camel/responses/agent_responses.py +1 -4
- camel/runtime/__init__.py +2 -0
- camel/runtime/daytona_runtime.py +248 -0
- camel/societies/role_playing.py +12 -0
- camel/toolkits/__init__.py +4 -0
- camel/toolkits/aci_toolkit.py +456 -0
- camel/toolkits/function_tool.py +5 -3
- camel/toolkits/klavis_toolkit.py +228 -0
- camel/toolkits/mcp_toolkit.py +63 -4
- camel/types/__init__.py +2 -0
- camel/types/enums.py +28 -0
- camel/utils/commons.py +2 -0
- camel/utils/mcp.py +63 -0
- {camel_ai-0.2.51.dist-info → camel_ai-0.2.53.dist-info}/METADATA +8 -1
- {camel_ai-0.2.51.dist-info → camel_ai-0.2.53.dist-info}/RECORD +26 -21
- {camel_ai-0.2.51.dist-info → camel_ai-0.2.53.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.51.dist-info → camel_ai-0.2.53.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,248 @@
|
|
|
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 inspect
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
from functools import wraps
|
|
19
|
+
from typing import Any, Dict, List, Optional, Union
|
|
20
|
+
|
|
21
|
+
from pydantic import BaseModel
|
|
22
|
+
|
|
23
|
+
from camel.logger import get_logger
|
|
24
|
+
from camel.runtime import BaseRuntime
|
|
25
|
+
from camel.toolkits.function_tool import FunctionTool
|
|
26
|
+
|
|
27
|
+
logger = get_logger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DaytonaRuntime(BaseRuntime):
|
|
31
|
+
r"""A runtime that executes functions in a Daytona sandbox environment.
|
|
32
|
+
Requires the Daytona server to be running and an API key configured.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
api_key (Optional[str]): The Daytona API key for authentication. If not
|
|
36
|
+
provided, it will try to use the DAYTONA_API_KEY environment
|
|
37
|
+
variable. (default: :obj: `None`)
|
|
38
|
+
api_url (Optional[str]): The URL of the Daytona server. If not
|
|
39
|
+
provided, it will try to use the DAYTONA_API_URL environment
|
|
40
|
+
variable. If none is provided, it will use "http://localhost:8000".
|
|
41
|
+
(default: :obj: `None`)
|
|
42
|
+
language (Optional[str]): The programming language for the sandbox.
|
|
43
|
+
(default: :obj: `"python"`)
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
api_key: Optional[str] = None,
|
|
49
|
+
api_url: Optional[str] = None,
|
|
50
|
+
language: Optional[str] = "python",
|
|
51
|
+
):
|
|
52
|
+
from daytona_sdk import Daytona, DaytonaConfig
|
|
53
|
+
|
|
54
|
+
super().__init__()
|
|
55
|
+
self.api_key = api_key or os.environ.get('DAYTONA_API_KEY')
|
|
56
|
+
self.api_url = api_url or os.environ.get('DAYTONA_API_URL')
|
|
57
|
+
self.language = language
|
|
58
|
+
self.config = DaytonaConfig(api_key=self.api_key, api_url=self.api_url)
|
|
59
|
+
self.daytona = Daytona(self.config)
|
|
60
|
+
self.sandbox = None
|
|
61
|
+
self.entrypoint: Dict[str, str] = dict()
|
|
62
|
+
|
|
63
|
+
def build(self) -> "DaytonaRuntime":
|
|
64
|
+
r"""Create and start a Daytona sandbox.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
DaytonaRuntime: The current runtime.
|
|
68
|
+
"""
|
|
69
|
+
from daytona_sdk import CreateSandboxParams
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
params = CreateSandboxParams(language=self.language)
|
|
73
|
+
self.sandbox = self.daytona.create(params)
|
|
74
|
+
if self.sandbox is None:
|
|
75
|
+
raise RuntimeError("Failed to create sandbox.")
|
|
76
|
+
logger.info(f"Sandbox created with ID: {self.sandbox.id}")
|
|
77
|
+
except Exception as e:
|
|
78
|
+
logger.error(f"Failed to create sandbox: {e!s}")
|
|
79
|
+
raise RuntimeError(f"Daytona sandbox creation failed: {e!s}")
|
|
80
|
+
return self
|
|
81
|
+
|
|
82
|
+
def _cleanup(self):
|
|
83
|
+
r"""Clean up the sandbox when exiting."""
|
|
84
|
+
if self.sandbox:
|
|
85
|
+
try:
|
|
86
|
+
self.daytona.remove(self.sandbox)
|
|
87
|
+
logger.info(f"Sandbox {self.sandbox.id} removed")
|
|
88
|
+
self.sandbox = None
|
|
89
|
+
except Exception as e:
|
|
90
|
+
logger.error(f"Failed to remove sandbox: {e!s}")
|
|
91
|
+
|
|
92
|
+
def add(
|
|
93
|
+
self,
|
|
94
|
+
funcs: Union[FunctionTool, List[FunctionTool]],
|
|
95
|
+
entrypoint: str,
|
|
96
|
+
arguments: Optional[Dict[str, Any]] = None,
|
|
97
|
+
) -> "DaytonaRuntime":
|
|
98
|
+
r"""Add a function or list of functions to the runtime.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
funcs (Union[FunctionTool, List[FunctionTool]]): The function or
|
|
102
|
+
list of functions to add.
|
|
103
|
+
entrypoint (str): The entrypoint for the function.
|
|
104
|
+
arguments (Optional[Dict[str, Any]]): The arguments for the
|
|
105
|
+
function. (default: :obj: `None`)
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
DaytonaRuntime: The current runtime.
|
|
109
|
+
"""
|
|
110
|
+
if not isinstance(funcs, list):
|
|
111
|
+
funcs = [funcs]
|
|
112
|
+
if arguments is not None:
|
|
113
|
+
entrypoint += json.dumps(arguments, ensure_ascii=False)
|
|
114
|
+
|
|
115
|
+
def make_wrapper(inner_func, func_name, func_code):
|
|
116
|
+
r"""Creates a wrapper for a function to execute it in the
|
|
117
|
+
Daytona sandbox.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
inner_func (Callable): The function to wrap.
|
|
121
|
+
func_name (str): The name of the function.
|
|
122
|
+
func_code (str): The source code of the function.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Callable: A wrapped function that executes in the sandbox.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
@wraps(inner_func)
|
|
129
|
+
def wrapper(*args, **kwargs):
|
|
130
|
+
if not self.sandbox:
|
|
131
|
+
raise RuntimeError(
|
|
132
|
+
"Sandbox not initialized. Call build() first."
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
for key, value in kwargs.items():
|
|
137
|
+
if isinstance(value, BaseModel):
|
|
138
|
+
kwargs[key] = value.model_dump()
|
|
139
|
+
args_str = json.dumps(args, ensure_ascii=True)
|
|
140
|
+
kwargs_str = json.dumps(kwargs, ensure_ascii=True)
|
|
141
|
+
except (TypeError, ValueError) as e:
|
|
142
|
+
logger.error(f"Failed to serialize arguments: {e!s}")
|
|
143
|
+
return {"error": f"Argument serialization failed: {e!s}"}
|
|
144
|
+
|
|
145
|
+
# Upload function code to the sandbox
|
|
146
|
+
script_path = f"/home/daytona/{func_name}.py"
|
|
147
|
+
try:
|
|
148
|
+
self.sandbox.fs.upload_file(
|
|
149
|
+
script_path, func_code.encode()
|
|
150
|
+
)
|
|
151
|
+
except Exception as e:
|
|
152
|
+
logger.error(
|
|
153
|
+
f"Failed to upload function {func_name}: {e!s}"
|
|
154
|
+
)
|
|
155
|
+
return {"error": f"Upload failed: {e!s}"}
|
|
156
|
+
|
|
157
|
+
exec_code = (
|
|
158
|
+
f"import sys\n"
|
|
159
|
+
f"sys.path.append('/home/daytona')\n"
|
|
160
|
+
f"import json\n"
|
|
161
|
+
f"from {func_name} import {func_name}\n"
|
|
162
|
+
f"args = json.loads('{args_str}')\n"
|
|
163
|
+
f"kwargs = json.loads('{kwargs_str}')\n"
|
|
164
|
+
f"result = {func_name}(*args, **kwargs)\n"
|
|
165
|
+
f"print(json.dumps(result) if result is not "
|
|
166
|
+
f"None else 'null')"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Execute the function in the sandbox
|
|
170
|
+
try:
|
|
171
|
+
response = self.sandbox.process.code_run(exec_code)
|
|
172
|
+
return (
|
|
173
|
+
json.loads(response.result)
|
|
174
|
+
if response.result
|
|
175
|
+
else None
|
|
176
|
+
)
|
|
177
|
+
except json.JSONDecodeError as e:
|
|
178
|
+
logger.error(
|
|
179
|
+
f"Failed to decode JSON response for {func_name}"
|
|
180
|
+
)
|
|
181
|
+
return {"error": f"JSON decoding failed: {e!s}"}
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logger.error(
|
|
184
|
+
f"Failed to execute function {func_name}: {e!s}"
|
|
185
|
+
)
|
|
186
|
+
return {"error": f"Execution failed: {e!s}"}
|
|
187
|
+
|
|
188
|
+
return wrapper
|
|
189
|
+
|
|
190
|
+
for func in funcs:
|
|
191
|
+
inner_func = func.func
|
|
192
|
+
func_name = func.get_function_name()
|
|
193
|
+
func_code = inspect.getsource(inner_func).strip()
|
|
194
|
+
|
|
195
|
+
func.func = make_wrapper(inner_func, func_name, func_code)
|
|
196
|
+
self.tools_map[func_name] = func
|
|
197
|
+
self.entrypoint[func_name] = entrypoint
|
|
198
|
+
|
|
199
|
+
return self
|
|
200
|
+
|
|
201
|
+
def info(self) -> str:
|
|
202
|
+
r"""Get information about the current sandbox.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
str: Information about the sandbox.
|
|
206
|
+
|
|
207
|
+
Raises:
|
|
208
|
+
RuntimeError: If the sandbox is not initialized.
|
|
209
|
+
"""
|
|
210
|
+
if self.sandbox is None:
|
|
211
|
+
raise RuntimeError("Failed to create sandbox.")
|
|
212
|
+
info = self.sandbox.info()
|
|
213
|
+
return (
|
|
214
|
+
f"Sandbox {info.name}:\n"
|
|
215
|
+
f"State: {info.state}\n"
|
|
216
|
+
f"Resources: {info.resources.cpu} CPU, {info.resources.memory} RAM"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
def __del__(self):
|
|
220
|
+
r"""Clean up the sandbox when the object is deleted."""
|
|
221
|
+
if hasattr(self, 'sandbox'):
|
|
222
|
+
self._cleanup()
|
|
223
|
+
|
|
224
|
+
def stop(self) -> "DaytonaRuntime":
|
|
225
|
+
r"""Stop and remove the sandbox.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
DaytonaRuntime: The current runtime.
|
|
229
|
+
"""
|
|
230
|
+
self._cleanup()
|
|
231
|
+
return self
|
|
232
|
+
|
|
233
|
+
def reset(self) -> "DaytonaRuntime":
|
|
234
|
+
r"""Reset the sandbox by stopping and rebuilding it.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
DaytonaRuntime: The current runtime.
|
|
238
|
+
"""
|
|
239
|
+
return self.stop().build()
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def docs(self) -> str:
|
|
243
|
+
r"""Get the URL for the Daytona API documentation.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
str: The URL for the API documentation.
|
|
247
|
+
"""
|
|
248
|
+
return "https://www.daytona.io/docs/python-sdk/daytona/"
|
camel/societies/role_playing.py
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
import logging
|
|
15
|
+
import threading
|
|
15
16
|
from typing import Dict, List, Optional, Sequence, Tuple, Union
|
|
16
17
|
|
|
17
18
|
from camel.agents import (
|
|
@@ -77,6 +78,9 @@ class RolePlaying:
|
|
|
77
78
|
task specify meta dict with. (default: :obj:`None`)
|
|
78
79
|
output_language (str, optional): The language to be output by the
|
|
79
80
|
agents. (default: :obj:`None`)
|
|
81
|
+
stop_event (Optional[threading.Event], optional): Event to signal
|
|
82
|
+
termination of the agent's operation. When set, the agent will
|
|
83
|
+
terminate its execution. (default: :obj:`None`)
|
|
80
84
|
"""
|
|
81
85
|
|
|
82
86
|
def __init__(
|
|
@@ -101,6 +105,7 @@ class RolePlaying:
|
|
|
101
105
|
extend_sys_msg_meta_dicts: Optional[List[Dict]] = None,
|
|
102
106
|
extend_task_specify_meta_dict: Optional[Dict] = None,
|
|
103
107
|
output_language: Optional[str] = None,
|
|
108
|
+
stop_event: Optional[threading.Event] = None,
|
|
104
109
|
) -> None:
|
|
105
110
|
if model is not None:
|
|
106
111
|
logger.warning(
|
|
@@ -156,6 +161,7 @@ class RolePlaying:
|
|
|
156
161
|
assistant_agent_kwargs=assistant_agent_kwargs,
|
|
157
162
|
user_agent_kwargs=user_agent_kwargs,
|
|
158
163
|
output_language=output_language,
|
|
164
|
+
stop_event=stop_event,
|
|
159
165
|
)
|
|
160
166
|
self.critic: Optional[Union[CriticAgent, Human]] = None
|
|
161
167
|
self.critic_sys_msg: Optional[BaseMessage] = None
|
|
@@ -316,6 +322,7 @@ class RolePlaying:
|
|
|
316
322
|
assistant_agent_kwargs: Optional[Dict] = None,
|
|
317
323
|
user_agent_kwargs: Optional[Dict] = None,
|
|
318
324
|
output_language: Optional[str] = None,
|
|
325
|
+
stop_event: Optional[threading.Event] = None,
|
|
319
326
|
) -> None:
|
|
320
327
|
r"""Initialize assistant and user agents with their system messages.
|
|
321
328
|
|
|
@@ -330,6 +337,9 @@ class RolePlaying:
|
|
|
330
337
|
pass to the user agent. (default: :obj:`None`)
|
|
331
338
|
output_language (str, optional): The language to be output by the
|
|
332
339
|
agents. (default: :obj:`None`)
|
|
340
|
+
stop_event (Optional[threading.Event], optional): Event to signal
|
|
341
|
+
termination of the agent's operation. When set, the agent will
|
|
342
|
+
terminate its execution. (default: :obj:`None`)
|
|
333
343
|
"""
|
|
334
344
|
if self.model is not None:
|
|
335
345
|
if assistant_agent_kwargs is None:
|
|
@@ -344,6 +354,7 @@ class RolePlaying:
|
|
|
344
354
|
self.assistant_agent = ChatAgent(
|
|
345
355
|
init_assistant_sys_msg,
|
|
346
356
|
output_language=output_language,
|
|
357
|
+
stop_event=stop_event,
|
|
347
358
|
**(assistant_agent_kwargs or {}),
|
|
348
359
|
)
|
|
349
360
|
self.assistant_sys_msg = self.assistant_agent.system_message
|
|
@@ -351,6 +362,7 @@ class RolePlaying:
|
|
|
351
362
|
self.user_agent = ChatAgent(
|
|
352
363
|
init_user_sys_msg,
|
|
353
364
|
output_language=output_language,
|
|
365
|
+
stop_event=stop_event,
|
|
354
366
|
**(user_agent_kwargs or {}),
|
|
355
367
|
)
|
|
356
368
|
self.user_sys_msg = self.user_agent.system_message
|
camel/toolkits/__init__.py
CHANGED
|
@@ -68,6 +68,8 @@ from .pyautogui_toolkit import PyAutoGUIToolkit
|
|
|
68
68
|
from .openai_agent_toolkit import OpenAIAgentToolkit
|
|
69
69
|
from .searxng_toolkit import SearxNGToolkit
|
|
70
70
|
from .jina_reranker_toolkit import JinaRerankerToolkit
|
|
71
|
+
from .klavis_toolkit import KlavisToolkit
|
|
72
|
+
from .aci_toolkit import ACIToolkit
|
|
71
73
|
|
|
72
74
|
|
|
73
75
|
__all__ = [
|
|
@@ -124,4 +126,6 @@ __all__ = [
|
|
|
124
126
|
'OpenAIAgentToolkit',
|
|
125
127
|
'SearxNGToolkit',
|
|
126
128
|
'JinaRerankerToolkit',
|
|
129
|
+
'KlavisToolkit',
|
|
130
|
+
'ACIToolkit',
|
|
127
131
|
]
|