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
|
@@ -0,0 +1,146 @@
|
|
|
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
|
+
import base64
|
|
15
|
+
import os
|
|
16
|
+
import uuid
|
|
17
|
+
from io import BytesIO
|
|
18
|
+
from typing import List, Optional
|
|
19
|
+
|
|
20
|
+
from openai import OpenAI
|
|
21
|
+
from PIL import Image
|
|
22
|
+
|
|
23
|
+
from camel.toolkits import OpenAIFunction
|
|
24
|
+
from camel.toolkits.base import BaseToolkit
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DalleToolkit(BaseToolkit):
|
|
28
|
+
r"""A class representing a toolkit for image generation using OpenAI's.
|
|
29
|
+
|
|
30
|
+
This class provides methods handle image generation using OpenAI's DALL-E.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def base64_to_image(self, base64_string: str) -> Optional[Image.Image]:
|
|
34
|
+
r"""Converts a base64 encoded string into a PIL Image object.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
base64_string (str): The base64 encoded string of the image.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Optional[Image.Image]: The PIL Image object or None if conversion
|
|
41
|
+
fails.
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
# Decode the base64 string to get the image data
|
|
45
|
+
image_data = base64.b64decode(base64_string)
|
|
46
|
+
# Create a memory buffer for the image data
|
|
47
|
+
image_buffer = BytesIO(image_data)
|
|
48
|
+
# Open the image using the PIL library
|
|
49
|
+
image = Image.open(image_buffer)
|
|
50
|
+
return image
|
|
51
|
+
except Exception as e:
|
|
52
|
+
print(f"An error occurred while converting base64 to image: {e}")
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
def image_path_to_base64(self, image_path: str) -> str:
|
|
56
|
+
r"""Converts the file path of an image to a Base64 encoded string.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
image_path (str): The path to the image file.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
str: A Base64 encoded string representing the content of the image
|
|
63
|
+
file.
|
|
64
|
+
"""
|
|
65
|
+
try:
|
|
66
|
+
with open(image_path, "rb") as image_file:
|
|
67
|
+
return base64.b64encode(image_file.read()).decode('utf-8')
|
|
68
|
+
except Exception as e:
|
|
69
|
+
print(
|
|
70
|
+
f"An error occurred while converting image path to base64: {e}"
|
|
71
|
+
)
|
|
72
|
+
return ""
|
|
73
|
+
|
|
74
|
+
def image_to_base64(self, image: Image.Image) -> str:
|
|
75
|
+
r"""Converts an image into a base64-encoded string.
|
|
76
|
+
|
|
77
|
+
This function takes an image object as input, encodes the image into a
|
|
78
|
+
PNG format base64 string, and returns it.
|
|
79
|
+
If the encoding process encounters an error, it prints the error
|
|
80
|
+
message and returns None.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
image: The image object to be encoded, supports any image format
|
|
84
|
+
that can be saved in PNG format.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
str: A base64-encoded string of the image.
|
|
88
|
+
"""
|
|
89
|
+
try:
|
|
90
|
+
with BytesIO() as buffered_image:
|
|
91
|
+
image.save(buffered_image, format="PNG")
|
|
92
|
+
buffered_image.seek(0)
|
|
93
|
+
image_bytes = buffered_image.read()
|
|
94
|
+
base64_str = base64.b64encode(image_bytes).decode('utf-8')
|
|
95
|
+
return base64_str
|
|
96
|
+
except Exception as e:
|
|
97
|
+
print(f"An error occurred: {e}")
|
|
98
|
+
return ""
|
|
99
|
+
|
|
100
|
+
def get_dalle_img(self, prompt: str, image_dir: str = "img") -> str:
|
|
101
|
+
r"""Generate an image using OpenAI's DALL-E model.
|
|
102
|
+
The generated image is saved to the specified directory.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
prompt (str): The text prompt based on which the image is
|
|
106
|
+
generated.
|
|
107
|
+
image_dir (str): The directory to save the generated image.
|
|
108
|
+
Defaults to 'img'.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
str: The path to the saved image.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
dalle_client = OpenAI()
|
|
115
|
+
response = dalle_client.images.generate(
|
|
116
|
+
model="dall-e-3",
|
|
117
|
+
prompt=prompt,
|
|
118
|
+
size="1024x1792",
|
|
119
|
+
quality="standard",
|
|
120
|
+
n=1, # NOTE: now dall-e-3 only supports n=1
|
|
121
|
+
response_format="b64_json",
|
|
122
|
+
)
|
|
123
|
+
image_b64 = response.data[0].b64_json
|
|
124
|
+
image = self.base64_to_image(image_b64) # type: ignore[arg-type]
|
|
125
|
+
|
|
126
|
+
if image is None:
|
|
127
|
+
raise ValueError("Failed to convert base64 string to image.")
|
|
128
|
+
|
|
129
|
+
os.makedirs(image_dir, exist_ok=True)
|
|
130
|
+
image_path = os.path.join(image_dir, f"{uuid.uuid4()}.png")
|
|
131
|
+
image.save(image_path)
|
|
132
|
+
|
|
133
|
+
return image_path
|
|
134
|
+
|
|
135
|
+
def get_tools(self) -> List[OpenAIFunction]:
|
|
136
|
+
r"""Returns a list of OpenAIFunction objects representing the
|
|
137
|
+
functions in the toolkit.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
List[OpenAIFunction]: A list of OpenAIFunction objects
|
|
141
|
+
representing the functions in the toolkit.
|
|
142
|
+
"""
|
|
143
|
+
return [OpenAIFunction(self.get_dalle_img)]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
DALLE_FUNCS: List[OpenAIFunction] = DalleToolkit().get_tools()
|
camel/toolkits/github_toolkit.py
CHANGED
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
14
|
|
|
15
15
|
import os
|
|
16
|
-
from dataclasses import dataclass
|
|
17
16
|
from datetime import datetime, timedelta
|
|
18
17
|
from typing import List, Optional
|
|
19
18
|
|
|
19
|
+
from pydantic import BaseModel
|
|
20
|
+
|
|
20
21
|
from camel.utils import dependencies_required
|
|
21
22
|
|
|
22
23
|
from .base import BaseToolkit
|
|
23
24
|
from .openai_function import OpenAIFunction
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
class GithubIssue:
|
|
27
|
+
class GithubIssue(BaseModel):
|
|
28
28
|
r"""Represents a GitHub issue.
|
|
29
29
|
|
|
30
30
|
Attributes:
|
|
@@ -35,29 +35,11 @@ class GithubIssue:
|
|
|
35
35
|
file_content (str): The content of the file associated with the issue.
|
|
36
36
|
"""
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
file_path: str,
|
|
44
|
-
file_content: str,
|
|
45
|
-
) -> None:
|
|
46
|
-
r"""Initialize a GithubIssue object.
|
|
47
|
-
|
|
48
|
-
Args:
|
|
49
|
-
title (str): The title of the GitHub issue.
|
|
50
|
-
body (str): The body/content of the GitHub issue.
|
|
51
|
-
number (int): The issue number.
|
|
52
|
-
file_path (str): The path of the file associated with the issue.
|
|
53
|
-
file_content (str): The content of the file associated with the
|
|
54
|
-
issue.
|
|
55
|
-
"""
|
|
56
|
-
self.title = title
|
|
57
|
-
self.body = body
|
|
58
|
-
self.number = number
|
|
59
|
-
self.file_path = file_path
|
|
60
|
-
self.file_content = file_content
|
|
38
|
+
title: str
|
|
39
|
+
body: str
|
|
40
|
+
number: int
|
|
41
|
+
file_path: str
|
|
42
|
+
file_content: str
|
|
61
43
|
|
|
62
44
|
def __str__(self) -> str:
|
|
63
45
|
r"""Returns a string representation of the issue.
|
|
@@ -75,8 +57,7 @@ class GithubIssue:
|
|
|
75
57
|
)
|
|
76
58
|
|
|
77
59
|
|
|
78
|
-
|
|
79
|
-
class GithubPullRequestDiff:
|
|
60
|
+
class GithubPullRequestDiff(BaseModel):
|
|
80
61
|
r"""Represents a single diff of a pull request on Github.
|
|
81
62
|
|
|
82
63
|
Attributes:
|
|
@@ -92,8 +73,7 @@ class GithubPullRequestDiff:
|
|
|
92
73
|
return f"Filename: {self.filename}\nPatch: {self.patch}"
|
|
93
74
|
|
|
94
75
|
|
|
95
|
-
|
|
96
|
-
class GithubPullRequest:
|
|
76
|
+
class GithubPullRequest(BaseModel):
|
|
97
77
|
r"""Represents a pull request on Github.
|
|
98
78
|
|
|
99
79
|
Attributes:
|
|
@@ -251,13 +231,17 @@ class GithubToolkit(BaseToolkit):
|
|
|
251
231
|
and pr.merged_at is not None
|
|
252
232
|
and pr.merged_at.timestamp() > earliest_date.timestamp()
|
|
253
233
|
):
|
|
254
|
-
pr_details = GithubPullRequest(
|
|
234
|
+
pr_details = GithubPullRequest(
|
|
235
|
+
title=pr.title, body=pr.body, diffs=[]
|
|
236
|
+
)
|
|
255
237
|
|
|
256
238
|
# Get files changed in the PR
|
|
257
239
|
files = pr.get_files()
|
|
258
240
|
|
|
259
241
|
for file in files:
|
|
260
|
-
diff = GithubPullRequestDiff(
|
|
242
|
+
diff = GithubPullRequestDiff(
|
|
243
|
+
filename=file.filename, patch=file.patch
|
|
244
|
+
)
|
|
261
245
|
pr_details.diffs.append(diff)
|
|
262
246
|
|
|
263
247
|
merged_prs.append(str(pr_details))
|
|
@@ -36,7 +36,8 @@ def handle_googlemaps_exceptions(
|
|
|
36
36
|
@wraps(func)
|
|
37
37
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
38
38
|
try:
|
|
39
|
-
|
|
39
|
+
# ruff: noqa: E501
|
|
40
|
+
from googlemaps.exceptions import ( # type: ignore[import]
|
|
40
41
|
ApiError,
|
|
41
42
|
HTTPError,
|
|
42
43
|
Timeout,
|
|
@@ -18,11 +18,10 @@ from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
|
18
18
|
import requests
|
|
19
19
|
|
|
20
20
|
from camel.toolkits import OpenAIFunction, openapi_security_config
|
|
21
|
-
from camel.toolkits.base import BaseToolkit
|
|
22
21
|
from camel.types import OpenAPIName
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
class OpenAPIToolkit
|
|
24
|
+
class OpenAPIToolkit:
|
|
26
25
|
r"""A class representing a toolkit for interacting with OpenAPI APIs.
|
|
27
26
|
|
|
28
27
|
This class provides methods for interacting with APIs based on OpenAPI
|
|
@@ -20,7 +20,7 @@ from jsonschema.validators import Draft202012Validator as JSONValidator
|
|
|
20
20
|
from pydantic import create_model
|
|
21
21
|
from pydantic.fields import FieldInfo
|
|
22
22
|
|
|
23
|
-
from camel.utils import
|
|
23
|
+
from camel.utils import get_pydantic_object_schema, to_pascal
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def _remove_a_key(d: Dict, remove_key: Any) -> None:
|
|
@@ -110,12 +110,7 @@ def get_openai_tool_schema(func: Callable) -> Dict[str, Any]:
|
|
|
110
110
|
return create_model(name, **field)
|
|
111
111
|
|
|
112
112
|
model = _create_mol(to_pascal(func.__name__), fields)
|
|
113
|
-
|
|
114
|
-
# the result would be identical to `.model_json_schema()` in v2
|
|
115
|
-
if PYDANTIC_V2:
|
|
116
|
-
parameters_dict = model.model_json_schema()
|
|
117
|
-
else:
|
|
118
|
-
parameters_dict = model.schema()
|
|
113
|
+
parameters_dict = get_pydantic_object_schema(model)
|
|
119
114
|
# The `"title"` is generated by `model.model_json_schema()`
|
|
120
115
|
# but is useless for openai json schema
|
|
121
116
|
_remove_a_key(parameters_dict, "title")
|
camel/types/enums.py
CHANGED
|
@@ -208,7 +208,6 @@ class ModelType(Enum):
|
|
|
208
208
|
# reference: https://lmsys.org/blog/2023-03-30-vicuna/
|
|
209
209
|
return 2048
|
|
210
210
|
elif self in {
|
|
211
|
-
ModelType.GPT_3_5_TURBO,
|
|
212
211
|
ModelType.LLAMA_2,
|
|
213
212
|
ModelType.NEMOTRON_4_REWARD,
|
|
214
213
|
ModelType.STUB,
|
|
@@ -226,7 +225,10 @@ class ModelType(Enum):
|
|
|
226
225
|
ModelType.GLM_4_OPEN_SOURCE,
|
|
227
226
|
}:
|
|
228
227
|
return 8_192
|
|
229
|
-
elif self
|
|
228
|
+
elif self in {
|
|
229
|
+
ModelType.GPT_3_5_TURBO,
|
|
230
|
+
ModelType.VICUNA_16K,
|
|
231
|
+
}:
|
|
230
232
|
return 16_384
|
|
231
233
|
elif self in {
|
|
232
234
|
ModelType.GPT_4_32K,
|
|
@@ -359,6 +361,8 @@ class TaskType(Enum):
|
|
|
359
361
|
ROLE_DESCRIPTION = "role_description"
|
|
360
362
|
GENERATE_TEXT_EMBEDDING_DATA = "generate_text_embedding_data"
|
|
361
363
|
OBJECT_RECOGNITION = "object_recognition"
|
|
364
|
+
IMAGE_CRAFT = "image_craft"
|
|
365
|
+
MULTI_CONDITION_IMAGE_CRAFT = "multi_condition_image_craft"
|
|
362
366
|
DEFAULT = "default"
|
|
363
367
|
VIDEO_DESCRIPTION = "video_description"
|
|
364
368
|
|
camel/utils/__init__.py
CHANGED
|
@@ -13,20 +13,26 @@
|
|
|
13
13
|
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
14
|
|
|
15
15
|
from .commons import (
|
|
16
|
-
|
|
16
|
+
AgentOpsMeta,
|
|
17
|
+
agentops_decorator,
|
|
17
18
|
api_keys_required,
|
|
18
19
|
check_server_running,
|
|
19
20
|
create_chunks,
|
|
20
21
|
dependencies_required,
|
|
21
22
|
download_tasks,
|
|
23
|
+
func_string_to_callable,
|
|
22
24
|
get_first_int,
|
|
23
25
|
get_prompt_template_key_words,
|
|
26
|
+
get_pydantic_major_version,
|
|
27
|
+
get_pydantic_object_schema,
|
|
24
28
|
get_system_information,
|
|
25
29
|
get_task_list,
|
|
26
30
|
is_docker_running,
|
|
31
|
+
json_to_function_code,
|
|
27
32
|
print_text_animated,
|
|
28
33
|
text_extract_from_web,
|
|
29
34
|
to_pascal,
|
|
35
|
+
track_agent,
|
|
30
36
|
)
|
|
31
37
|
from .constants import Constants
|
|
32
38
|
from .token_counting import (
|
|
@@ -50,7 +56,6 @@ __all__ = [
|
|
|
50
56
|
'AnthropicTokenCounter',
|
|
51
57
|
'get_system_information',
|
|
52
58
|
'to_pascal',
|
|
53
|
-
'PYDANTIC_V2',
|
|
54
59
|
'get_model_encoding',
|
|
55
60
|
'BaseTokenCounter',
|
|
56
61
|
'OpenAITokenCounter',
|
|
@@ -64,4 +69,11 @@ __all__ = [
|
|
|
64
69
|
'is_docker_running',
|
|
65
70
|
'GeminiTokenCounter',
|
|
66
71
|
'MistralTokenCounter',
|
|
72
|
+
'get_pydantic_major_version',
|
|
73
|
+
'get_pydantic_object_schema',
|
|
74
|
+
'func_string_to_callable',
|
|
75
|
+
'json_to_function_code',
|
|
76
|
+
'agentops_decorator',
|
|
77
|
+
'AgentOpsMeta',
|
|
78
|
+
'track_agent',
|
|
67
79
|
]
|
camel/utils/commons.py
CHANGED
|
@@ -20,14 +20,27 @@ import subprocess
|
|
|
20
20
|
import time
|
|
21
21
|
import zipfile
|
|
22
22
|
from functools import wraps
|
|
23
|
-
from typing import
|
|
23
|
+
from typing import (
|
|
24
|
+
Any,
|
|
25
|
+
Callable,
|
|
26
|
+
Dict,
|
|
27
|
+
List,
|
|
28
|
+
Mapping,
|
|
29
|
+
Optional,
|
|
30
|
+
Set,
|
|
31
|
+
TypeVar,
|
|
32
|
+
cast,
|
|
33
|
+
)
|
|
24
34
|
from urllib.parse import urlparse
|
|
25
35
|
|
|
26
36
|
import pydantic
|
|
27
37
|
import requests
|
|
38
|
+
from pydantic import BaseModel
|
|
28
39
|
|
|
29
40
|
from camel.types import TaskType
|
|
30
41
|
|
|
42
|
+
from .constants import Constants
|
|
43
|
+
|
|
31
44
|
F = TypeVar('F', bound=Callable[..., Any])
|
|
32
45
|
|
|
33
46
|
|
|
@@ -303,7 +316,97 @@ def to_pascal(snake: str) -> str:
|
|
|
303
316
|
)
|
|
304
317
|
|
|
305
318
|
|
|
306
|
-
|
|
319
|
+
def get_pydantic_major_version() -> int:
|
|
320
|
+
r"""Get the major version of Pydantic.
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
int: The major version number of Pydantic if installed, otherwise 0.
|
|
324
|
+
"""
|
|
325
|
+
try:
|
|
326
|
+
return int(pydantic.__version__.split(".")[0])
|
|
327
|
+
except ImportError:
|
|
328
|
+
return 0
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def get_pydantic_object_schema(pydantic_params: BaseModel) -> Dict:
|
|
332
|
+
r"""Get the JSON schema of a Pydantic model.
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
pydantic_params (BaseModel): The Pydantic model to retrieve the schema
|
|
336
|
+
for.
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
dict: The JSON schema of the Pydantic model.
|
|
340
|
+
"""
|
|
341
|
+
return pydantic_params.model_json_schema()
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def func_string_to_callable(code: str):
|
|
345
|
+
r"""Convert a function code string to a callable function object.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
code (str): The function code as a string.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Callable[..., Any]: The callable function object extracted from the
|
|
352
|
+
code string.
|
|
353
|
+
"""
|
|
354
|
+
local_vars: Mapping[str, object] = {}
|
|
355
|
+
exec(code, globals(), local_vars)
|
|
356
|
+
func = local_vars.get(Constants.FUNC_NAME_FOR_STRUCTURE_OUTPUT)
|
|
357
|
+
return func
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def json_to_function_code(json_obj: Dict) -> str:
|
|
361
|
+
r"""Generate a Python function code from a JSON schema.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
json_obj (dict): The JSON schema object containing properties and
|
|
365
|
+
required fields, and json format is follow openai tools schema
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
str: The generated Python function code as a string.
|
|
369
|
+
"""
|
|
370
|
+
properties = json_obj.get('properties', {})
|
|
371
|
+
required = json_obj.get('required', [])
|
|
372
|
+
|
|
373
|
+
if not properties or not required:
|
|
374
|
+
raise ValueError(
|
|
375
|
+
"JSON schema must contain 'properties' and 'required' fields"
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
args = []
|
|
379
|
+
docstring_args = []
|
|
380
|
+
return_keys = []
|
|
381
|
+
|
|
382
|
+
for prop in required:
|
|
383
|
+
description = properties[prop]['description']
|
|
384
|
+
prop_type = properties[prop]['type']
|
|
385
|
+
python_type = 'str' if prop_type == 'string' else prop_type
|
|
386
|
+
args.append(f"{prop}: {python_type}")
|
|
387
|
+
docstring_args.append(
|
|
388
|
+
f" {prop} ({python_type}): {description}."
|
|
389
|
+
)
|
|
390
|
+
return_keys.append(prop)
|
|
391
|
+
|
|
392
|
+
# extract entity of schema
|
|
393
|
+
args_str = ", ".join(args)
|
|
394
|
+
docstring_args_str = "\n".join(docstring_args)
|
|
395
|
+
return_keys_str = ", ".join(return_keys)
|
|
396
|
+
|
|
397
|
+
# function template
|
|
398
|
+
function_code = f'''
|
|
399
|
+
def {Constants.FUNC_NAME_FOR_STRUCTURE_OUTPUT}({args_str}):
|
|
400
|
+
r"""Return response with a specified json format.
|
|
401
|
+
Args:
|
|
402
|
+
{docstring_args_str}
|
|
403
|
+
Returns:
|
|
404
|
+
Dict: A dictionary containing {return_keys_str}.
|
|
405
|
+
"""
|
|
406
|
+
return {{{", ".join([f'"{prop}": {prop}' for prop in required])}}}
|
|
407
|
+
'''
|
|
408
|
+
|
|
409
|
+
return function_code
|
|
307
410
|
|
|
308
411
|
|
|
309
412
|
def text_extract_from_web(url: str) -> str:
|
|
@@ -382,3 +485,65 @@ def is_docker_running() -> bool:
|
|
|
382
485
|
return result.returncode == 0
|
|
383
486
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
384
487
|
return False
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
try:
|
|
491
|
+
if os.getenv("AGENTOPS_API_KEY") is not None:
|
|
492
|
+
from agentops import (
|
|
493
|
+
ToolEvent,
|
|
494
|
+
record,
|
|
495
|
+
)
|
|
496
|
+
else:
|
|
497
|
+
raise ImportError
|
|
498
|
+
except (ImportError, AttributeError):
|
|
499
|
+
ToolEvent = None
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def agentops_decorator(func):
|
|
503
|
+
r"""Decorator that records the execution of a function if ToolEvent is
|
|
504
|
+
available.
|
|
505
|
+
|
|
506
|
+
Parameters:
|
|
507
|
+
func (callable): The function to be decorated.
|
|
508
|
+
|
|
509
|
+
Returns:
|
|
510
|
+
callable: The wrapped function which records its execution details.
|
|
511
|
+
"""
|
|
512
|
+
|
|
513
|
+
@wraps(func)
|
|
514
|
+
def wrapper(*args, **kwargs):
|
|
515
|
+
if ToolEvent:
|
|
516
|
+
tool_event = ToolEvent(name=func.__name__, params=kwargs)
|
|
517
|
+
result = func(*args, **kwargs)
|
|
518
|
+
tool_event.returns = result
|
|
519
|
+
record(tool_event)
|
|
520
|
+
return result
|
|
521
|
+
return func(*args, **kwargs)
|
|
522
|
+
|
|
523
|
+
return wrapper
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
class AgentOpsMeta(type):
|
|
527
|
+
r"""Metaclass that automatically decorates all callable attributes with
|
|
528
|
+
the agentops_decorator,
|
|
529
|
+
except for the 'get_tools' method.
|
|
530
|
+
|
|
531
|
+
Methods:
|
|
532
|
+
__new__(cls, name, bases, dct):
|
|
533
|
+
Creates a new class with decorated methods.
|
|
534
|
+
"""
|
|
535
|
+
|
|
536
|
+
def __new__(cls, name, bases, dct):
|
|
537
|
+
if ToolEvent:
|
|
538
|
+
for attr, value in dct.items():
|
|
539
|
+
if callable(value) and attr != 'get_tools':
|
|
540
|
+
dct[attr] = agentops_decorator(value)
|
|
541
|
+
return super().__new__(cls, name, bases, dct)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
# Mock trak agent decorator for AgentOps
|
|
545
|
+
def track_agent(*args, **kwargs):
|
|
546
|
+
def noop(f):
|
|
547
|
+
return f
|
|
548
|
+
|
|
549
|
+
return noop
|
camel/utils/constants.py
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
from .base import BaseNode
|
|
16
|
+
from .manager_node import ManagerNode
|
|
17
|
+
from .worker_node import WorkerNode
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"BaseNode",
|
|
21
|
+
"WorkerNode",
|
|
22
|
+
"ManagerNode",
|
|
23
|
+
]
|
camel/workforce/base.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
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 abc import ABC, abstractmethod
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from camel.workforce.task_channel import TaskChannel
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseNode(ABC):
|
|
21
|
+
def __init__(self, description: str) -> None:
|
|
22
|
+
self.node_id = str(id(self))
|
|
23
|
+
self.description = description
|
|
24
|
+
# every node is initialized to use its own channel
|
|
25
|
+
self._channel: TaskChannel = TaskChannel()
|
|
26
|
+
self._running = False
|
|
27
|
+
|
|
28
|
+
def reset(self, *args: Any, **kwargs: Any) -> Any:
|
|
29
|
+
"""Resets the node to its initial state."""
|
|
30
|
+
raise NotImplementedError()
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def set_channel(self, channel: TaskChannel):
|
|
34
|
+
r"""Sets the channel for the node."""
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
async def _listen_to_channel(self):
|
|
38
|
+
r"""Listens to the channel and handle tasks. This method should be
|
|
39
|
+
the main loop for the node.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
async def start(self):
|
|
44
|
+
r"""Start the node."""
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def stop(self):
|
|
48
|
+
r"""
|
|
49
|
+
Stop the node.
|
|
50
|
+
"""
|