camel-ai 0.2.60__py3-none-any.whl → 0.2.62__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 +159 -8
- camel/agents/mcp_agent.py +5 -5
- camel/configs/anthropic_config.py +6 -5
- camel/{data_collector → data_collectors}/alpaca_collector.py +1 -1
- camel/{data_collector → data_collectors}/sharegpt_collector.py +1 -1
- camel/datagen/evol_instruct/scorer.py +22 -23
- camel/datagen/evol_instruct/templates.py +46 -46
- camel/datasets/static_dataset.py +144 -0
- camel/loaders/__init__.py +5 -2
- camel/loaders/chunkr_reader.py +117 -91
- camel/loaders/mistral_reader.py +148 -0
- camel/memories/blocks/chat_history_block.py +1 -2
- camel/models/model_manager.py +7 -3
- camel/retrievers/auto_retriever.py +20 -1
- camel/{runtime → runtimes}/daytona_runtime.py +1 -1
- camel/{runtime → runtimes}/docker_runtime.py +1 -1
- camel/{runtime → runtimes}/llm_guard_runtime.py +2 -2
- camel/{runtime → runtimes}/remote_http_runtime.py +1 -1
- camel/{runtime → runtimes}/ubuntu_docker_runtime.py +1 -1
- camel/societies/workforce/base.py +7 -3
- camel/societies/workforce/single_agent_worker.py +2 -1
- camel/societies/workforce/worker.py +5 -3
- camel/societies/workforce/workforce.py +65 -24
- camel/storages/__init__.py +2 -0
- camel/storages/vectordb_storages/__init__.py +2 -0
- camel/storages/vectordb_storages/faiss.py +712 -0
- camel/toolkits/__init__.py +4 -0
- camel/toolkits/async_browser_toolkit.py +75 -523
- camel/toolkits/bohrium_toolkit.py +318 -0
- camel/toolkits/browser_toolkit.py +215 -538
- camel/toolkits/browser_toolkit_commons.py +568 -0
- camel/toolkits/file_write_toolkit.py +80 -31
- camel/toolkits/mcp_toolkit.py +477 -665
- camel/toolkits/pptx_toolkit.py +777 -0
- camel/toolkits/wolfram_alpha_toolkit.py +5 -1
- camel/types/enums.py +13 -1
- camel/utils/__init__.py +2 -0
- camel/utils/commons.py +27 -0
- camel/utils/mcp_client.py +979 -0
- {camel_ai-0.2.60.dist-info → camel_ai-0.2.62.dist-info}/METADATA +14 -1
- {camel_ai-0.2.60.dist-info → camel_ai-0.2.62.dist-info}/RECORD +53 -47
- /camel/{data_collector → data_collectors}/__init__.py +0 -0
- /camel/{data_collector → data_collectors}/base.py +0 -0
- /camel/{runtime → runtimes}/__init__.py +0 -0
- /camel/{runtime → runtimes}/api.py +0 -0
- /camel/{runtime → runtimes}/base.py +0 -0
- /camel/{runtime → runtimes}/configs.py +0 -0
- /camel/{runtime → runtimes}/utils/__init__.py +0 -0
- /camel/{runtime → runtimes}/utils/function_risk_toolkit.py +0 -0
- /camel/{runtime → runtimes}/utils/ignore_risk_toolkit.py +0 -0
- {camel_ai-0.2.60.dist-info → camel_ai-0.2.62.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.60.dist-info → camel_ai-0.2.62.dist-info}/licenses/LICENSE +0 -0
camel/loaders/chunkr_reader.py
CHANGED
|
@@ -13,16 +13,38 @@
|
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
|
|
15
15
|
import json
|
|
16
|
-
import logging
|
|
17
16
|
import os
|
|
18
|
-
import
|
|
19
|
-
from typing import IO, Any, Optional, Union
|
|
17
|
+
from typing import TYPE_CHECKING, Optional
|
|
20
18
|
|
|
21
|
-
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from chunkr_ai.models import Configuration
|
|
22
21
|
|
|
22
|
+
from camel.logger import get_logger
|
|
23
23
|
from camel.utils import api_keys_required
|
|
24
24
|
|
|
25
|
-
logger =
|
|
25
|
+
logger = get_logger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ChunkrReaderConfig:
|
|
29
|
+
r"""Defines the parameters for configuring the task.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
chunk_processing (int, optional): The target chunk length.
|
|
33
|
+
(default: :obj:`512`)
|
|
34
|
+
high_resolution (bool, optional): Whether to use high resolution OCR.
|
|
35
|
+
(default: :obj:`True`)
|
|
36
|
+
ocr_strategy (str, optional): The OCR strategy. Defaults to 'Auto'.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
chunk_processing: int = 512,
|
|
42
|
+
high_resolution: bool = True,
|
|
43
|
+
ocr_strategy: str = "Auto",
|
|
44
|
+
):
|
|
45
|
+
self.chunk_processing = chunk_processing
|
|
46
|
+
self.high_resolution = high_resolution
|
|
47
|
+
self.ocr_strategy = ocr_strategy
|
|
26
48
|
|
|
27
49
|
|
|
28
50
|
class ChunkrReader:
|
|
@@ -35,8 +57,6 @@ class ChunkrReader:
|
|
|
35
57
|
`CHUNKR_API_KEY`. (default: :obj:`None`)
|
|
36
58
|
url (Optional[str], optional): The url to the Chunkr service.
|
|
37
59
|
(default: :obj:`https://api.chunkr.ai/api/v1/task`)
|
|
38
|
-
timeout (int, optional): The maximum time in seconds to wait for the
|
|
39
|
-
API responses. (default: :obj:`30`)
|
|
40
60
|
**kwargs (Any): Additional keyword arguments for request headers.
|
|
41
61
|
"""
|
|
42
62
|
|
|
@@ -49,111 +69,80 @@ class ChunkrReader:
|
|
|
49
69
|
self,
|
|
50
70
|
api_key: Optional[str] = None,
|
|
51
71
|
url: Optional[str] = "https://api.chunkr.ai/api/v1/task",
|
|
52
|
-
timeout: int = 30,
|
|
53
|
-
**kwargs: Any,
|
|
54
72
|
) -> None:
|
|
73
|
+
from chunkr_ai import Chunkr
|
|
74
|
+
|
|
55
75
|
self._api_key = api_key or os.getenv('CHUNKR_API_KEY')
|
|
56
|
-
self.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
**kwargs,
|
|
60
|
-
}
|
|
61
|
-
self.timeout = timeout
|
|
62
|
-
|
|
63
|
-
def submit_task(
|
|
76
|
+
self._chunkr = Chunkr(api_key=self._api_key)
|
|
77
|
+
|
|
78
|
+
async def submit_task(
|
|
64
79
|
self,
|
|
65
80
|
file_path: str,
|
|
66
|
-
|
|
67
|
-
ocr_strategy: str = "Auto",
|
|
68
|
-
target_chunk_length: str = "512",
|
|
81
|
+
chunkr_config: Optional[ChunkrReaderConfig] = None,
|
|
69
82
|
) -> str:
|
|
70
83
|
r"""Submits a file to the Chunkr API and returns the task ID.
|
|
71
84
|
|
|
72
85
|
Args:
|
|
73
86
|
file_path (str): The path to the file to be uploaded.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
ocr_strategy (str, optional): The OCR strategy. Defaults to 'Auto'.
|
|
77
|
-
target_chunk_length (str, optional): The target chunk length.
|
|
78
|
-
(default: :obj:`512`)
|
|
87
|
+
chunkr_config (ChunkrReaderConfig, optional): The configuration
|
|
88
|
+
for the Chunkr API. Defaults to None.
|
|
79
89
|
|
|
80
90
|
Returns:
|
|
81
91
|
str: The task ID.
|
|
82
92
|
"""
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
timeout=self.timeout,
|
|
101
|
-
)
|
|
102
|
-
response.raise_for_status()
|
|
103
|
-
task_id = response.json().get('task_id')
|
|
104
|
-
if not task_id:
|
|
105
|
-
raise ValueError("Task ID not returned in the response.")
|
|
106
|
-
logger.info(f"Task submitted successfully. Task ID: {task_id}")
|
|
107
|
-
return task_id
|
|
108
|
-
except Exception as e:
|
|
109
|
-
logger.error(f"Failed to submit task: {e}")
|
|
110
|
-
raise ValueError(f"Failed to submit task: {e}") from e
|
|
111
|
-
|
|
112
|
-
def get_task_output(self, task_id: str, max_retries: int = 5) -> str:
|
|
93
|
+
chunkr_config = self._to_chunkr_configuration(
|
|
94
|
+
chunkr_config or ChunkrReaderConfig()
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
task = await self._chunkr.create_task(
|
|
99
|
+
file=file_path, config=chunkr_config
|
|
100
|
+
)
|
|
101
|
+
logger.info(
|
|
102
|
+
f"Task submitted successfully. Task ID: {task.task_id}"
|
|
103
|
+
)
|
|
104
|
+
return task.task_id
|
|
105
|
+
except Exception as e:
|
|
106
|
+
logger.error(f"Failed to submit task: {e}")
|
|
107
|
+
raise ValueError(f"Failed to submit task: {e}") from e
|
|
108
|
+
|
|
109
|
+
async def get_task_output(self, task_id: str) -> str | None:
|
|
113
110
|
r"""Polls the Chunkr API to check the task status and returns the task
|
|
114
111
|
result.
|
|
115
112
|
|
|
116
113
|
Args:
|
|
117
114
|
task_id (str): The task ID to check the status for.
|
|
118
|
-
max_retries (int, optional): Maximum number of retry attempts.
|
|
119
|
-
(default: :obj:`5`)
|
|
120
115
|
|
|
121
116
|
Returns:
|
|
122
|
-
str: The formatted task result in JSON format
|
|
123
|
-
|
|
124
|
-
Raises:
|
|
125
|
-
ValueError: If the task status cannot be retrieved.
|
|
126
|
-
RuntimeError: If the maximum number of retries is reached without
|
|
127
|
-
a successful task completion.
|
|
117
|
+
Optional[str]: The formatted task result in JSON format, or `None`
|
|
118
|
+
if the task fails or is canceld.
|
|
128
119
|
"""
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
120
|
+
from chunkr_ai.models import Status
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
task = await self._chunkr.get_task(task_id)
|
|
124
|
+
except Exception as e:
|
|
125
|
+
logger.error(f"Failed to get task by task id: {task_id}: {e}")
|
|
126
|
+
raise ValueError(
|
|
127
|
+
f"Failed to get task by task id: {task_id}: {e}"
|
|
128
|
+
) from e
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
await task.poll()
|
|
132
|
+
if task.status == Status.SUCCEEDED:
|
|
133
|
+
logger.info(f"Task {task_id} completed successfully.")
|
|
134
|
+
return self._pretty_print_response(task.json())
|
|
135
|
+
elif task.status == Status.FAILED:
|
|
136
|
+
logger.warning(
|
|
137
|
+
f"Task {task_id} encountered an error: {task.message}"
|
|
136
138
|
)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
logger.info(
|
|
145
|
-
f"Task {task_id} is still {task_status}. Retrying "
|
|
146
|
-
"in 5 seconds..."
|
|
147
|
-
)
|
|
148
|
-
except Exception as e:
|
|
149
|
-
logger.error(f"Failed to retrieve task status: {e}")
|
|
150
|
-
raise ValueError(f"Failed to retrieve task status: {e}") from e
|
|
151
|
-
|
|
152
|
-
attempts += 1
|
|
153
|
-
time.sleep(5)
|
|
154
|
-
|
|
155
|
-
logger.error(f"Max retries reached for task {task_id}.")
|
|
156
|
-
raise RuntimeError(f"Max retries reached for task {task_id}.")
|
|
139
|
+
return None
|
|
140
|
+
else:
|
|
141
|
+
logger.warning(f"Task {task_id} was manually cancelled.")
|
|
142
|
+
return None
|
|
143
|
+
except Exception as e:
|
|
144
|
+
logger.error(f"Failed to retrieve task status: {e}")
|
|
145
|
+
raise ValueError(f"Failed to retrieve task status: {e}") from e
|
|
157
146
|
|
|
158
147
|
def _pretty_print_response(self, response_json: dict) -> str:
|
|
159
148
|
r"""Pretty prints the JSON response.
|
|
@@ -164,4 +153,41 @@ class ChunkrReader:
|
|
|
164
153
|
Returns:
|
|
165
154
|
str: Formatted JSON as a string.
|
|
166
155
|
"""
|
|
167
|
-
|
|
156
|
+
from datetime import datetime
|
|
157
|
+
|
|
158
|
+
return json.dumps(
|
|
159
|
+
response_json,
|
|
160
|
+
default=lambda o: o.isoformat()
|
|
161
|
+
if isinstance(o, datetime)
|
|
162
|
+
else None,
|
|
163
|
+
indent=4,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
def _to_chunkr_configuration(
|
|
167
|
+
self, chunkr_config: ChunkrReaderConfig
|
|
168
|
+
) -> "Configuration":
|
|
169
|
+
r"""Converts the ChunkrReaderConfig to Chunkr Configuration.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
chunkr_config (ChunkrReaderConfig):
|
|
173
|
+
The ChunkrReaderConfig to convert.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Configuration: Chunkr SDK configuration.
|
|
177
|
+
"""
|
|
178
|
+
from chunkr_ai.models import (
|
|
179
|
+
ChunkProcessing,
|
|
180
|
+
Configuration,
|
|
181
|
+
OcrStrategy,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
return Configuration(
|
|
185
|
+
chunk_processing=ChunkProcessing(
|
|
186
|
+
target_length=chunkr_config.chunk_processing
|
|
187
|
+
),
|
|
188
|
+
high_resolution=chunkr_config.high_resolution,
|
|
189
|
+
ocr_strategy={
|
|
190
|
+
"Auto": OcrStrategy.AUTO,
|
|
191
|
+
"All": OcrStrategy.ALL,
|
|
192
|
+
}.get(chunkr_config.ocr_strategy, OcrStrategy.ALL),
|
|
193
|
+
)
|
|
@@ -0,0 +1,148 @@
|
|
|
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
|
+
import os
|
|
15
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from mistralai.models import OCRResponse
|
|
19
|
+
|
|
20
|
+
from camel.logger import get_logger
|
|
21
|
+
from camel.utils import api_keys_required
|
|
22
|
+
|
|
23
|
+
logger = get_logger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class MistralReader:
|
|
27
|
+
r"""Mistral Document Loader."""
|
|
28
|
+
|
|
29
|
+
@api_keys_required(
|
|
30
|
+
[
|
|
31
|
+
("api_key", "MISTRAL_API_KEY"),
|
|
32
|
+
]
|
|
33
|
+
)
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
api_key: Optional[str] = None,
|
|
37
|
+
model: Optional[str] = "mistral-ocr-latest",
|
|
38
|
+
) -> None:
|
|
39
|
+
r"""Initialize the MistralReader.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
api_key (Optional[str]): The API key for the Mistral API.
|
|
43
|
+
(default: :obj:`None`)
|
|
44
|
+
model (Optional[str]): The model to use for OCR.
|
|
45
|
+
(default: :obj:`"mistral-ocr-latest"`)
|
|
46
|
+
"""
|
|
47
|
+
from mistralai import Mistral
|
|
48
|
+
|
|
49
|
+
self._api_key = api_key or os.environ.get("MISTRAL_API_KEY")
|
|
50
|
+
self.client = Mistral(api_key=self._api_key)
|
|
51
|
+
self.model = model
|
|
52
|
+
|
|
53
|
+
def _encode_file(self, file_path: str) -> str:
|
|
54
|
+
r"""Encode the pdf to base64.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
file_path (str): Path to the input file.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
str: base64 version of the file.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
import base64
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
with open(file_path, "rb") as pdf_file:
|
|
67
|
+
return base64.b64encode(pdf_file.read()).decode('utf-8')
|
|
68
|
+
except FileNotFoundError:
|
|
69
|
+
logger.error(f"Error: The file {file_path} was not found.")
|
|
70
|
+
return ""
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.error(f"Error: {e}")
|
|
73
|
+
return ""
|
|
74
|
+
|
|
75
|
+
def extract_text(
|
|
76
|
+
self,
|
|
77
|
+
file_path: str,
|
|
78
|
+
is_image: bool = False,
|
|
79
|
+
pages: Optional[List[int]] = None,
|
|
80
|
+
include_image_base64: Optional[bool] = None,
|
|
81
|
+
) -> "OCRResponse":
|
|
82
|
+
r"""Converts the given file to Markdown format.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
file_path (str): Path to the input file or a remote URL.
|
|
86
|
+
is_image (bool): Whether the file or URL is an image. If True,
|
|
87
|
+
uses image_url type instead of document_url.
|
|
88
|
+
(default: :obj:`False`)
|
|
89
|
+
pages (Optional[List[int]]): Specific pages user wants to process
|
|
90
|
+
in various formats: single number, range, or list of both.
|
|
91
|
+
Starts from 0. (default: :obj:`None`)
|
|
92
|
+
include_image_base64 (Optional[bool]): Whether to include image
|
|
93
|
+
URLs in response. (default: :obj:`None`)
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
OCRResponse: page wise extractions.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
FileNotFoundError: If the specified local file does not exist.
|
|
100
|
+
ValueError: If the file format is not supported.
|
|
101
|
+
Exception: For other errors during conversion.
|
|
102
|
+
"""
|
|
103
|
+
# Check if the input is a URL (starts with http:// or https://)
|
|
104
|
+
is_url = file_path.startswith(('http://', 'https://'))
|
|
105
|
+
|
|
106
|
+
if not is_url and not os.path.isfile(file_path):
|
|
107
|
+
logger.error(f"File not found: {file_path}")
|
|
108
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
109
|
+
try:
|
|
110
|
+
if is_url:
|
|
111
|
+
logger.info(f"Processing URL: {file_path}")
|
|
112
|
+
if is_image:
|
|
113
|
+
document_config = {
|
|
114
|
+
"type": "image_url",
|
|
115
|
+
"image_url": file_path,
|
|
116
|
+
}
|
|
117
|
+
else:
|
|
118
|
+
document_config = {
|
|
119
|
+
"type": "document_url",
|
|
120
|
+
"document_url": file_path,
|
|
121
|
+
}
|
|
122
|
+
else:
|
|
123
|
+
logger.info(f"Converting local file: {file_path}")
|
|
124
|
+
base64_file = self._encode_file(file_path)
|
|
125
|
+
if is_image:
|
|
126
|
+
document_config = {
|
|
127
|
+
"type": "image_url",
|
|
128
|
+
"image_url": f"data:image/jpeg;base64,{base64_file}",
|
|
129
|
+
}
|
|
130
|
+
else:
|
|
131
|
+
document_config = {
|
|
132
|
+
"type": "document_url",
|
|
133
|
+
"document_url": f"data:application/"
|
|
134
|
+
f"pdf;base64,{base64_file}",
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
ocr_response = self.client.ocr.process(
|
|
138
|
+
model=self.model,
|
|
139
|
+
document=document_config, # type: ignore[arg-type]
|
|
140
|
+
pages=None if is_image else pages,
|
|
141
|
+
include_image_base64=include_image_base64,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
logger.info(f"Processing completed successfully for: {file_path}")
|
|
145
|
+
return ocr_response
|
|
146
|
+
except Exception as e:
|
|
147
|
+
logger.error(f"Error processing '{file_path}': {e}")
|
|
148
|
+
raise ValueError(f"Error processing '{file_path}': {e}")
|
|
@@ -73,7 +73,6 @@ class ChatHistoryBlock(MemoryBlock):
|
|
|
73
73
|
warnings.warn("The `ChatHistoryMemory` is empty.")
|
|
74
74
|
return list()
|
|
75
75
|
|
|
76
|
-
chat_records: List[MemoryRecord] = []
|
|
77
76
|
if window_size is not None and window_size >= 0:
|
|
78
77
|
# Initial preserved index: Keep first message
|
|
79
78
|
# if it's SYSTEM/DEVELOPER (index 0)
|
|
@@ -117,7 +116,7 @@ class ChatHistoryBlock(MemoryBlock):
|
|
|
117
116
|
# Return full records when no window restriction
|
|
118
117
|
final_records = record_dicts
|
|
119
118
|
|
|
120
|
-
chat_records = [
|
|
119
|
+
chat_records: List[MemoryRecord] = [
|
|
121
120
|
MemoryRecord.from_dict(record) for record in final_records
|
|
122
121
|
]
|
|
123
122
|
|
camel/models/model_manager.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
|
|
|
15
|
+
import asyncio
|
|
15
16
|
import logging
|
|
16
17
|
from itertools import cycle
|
|
17
18
|
from random import choice
|
|
@@ -69,6 +70,7 @@ class ModelManager:
|
|
|
69
70
|
self.models = [models]
|
|
70
71
|
self.models_cycle = cycle(self.models)
|
|
71
72
|
self.current_model = self.models[0]
|
|
73
|
+
self.lock = asyncio.Lock()
|
|
72
74
|
|
|
73
75
|
# Set the scheduling strategy; default is round-robin
|
|
74
76
|
try:
|
|
@@ -246,7 +248,8 @@ class ModelManager:
|
|
|
246
248
|
`ChatCompletion` in the non-stream mode, or
|
|
247
249
|
`AsyncStream[ChatCompletionChunk]` in the stream mode.
|
|
248
250
|
"""
|
|
249
|
-
|
|
251
|
+
async with self.lock:
|
|
252
|
+
self.current_model = self.scheduling_strategy()
|
|
250
253
|
|
|
251
254
|
# Pass all messages to the selected model and get the response
|
|
252
255
|
try:
|
|
@@ -260,7 +263,8 @@ class ModelManager:
|
|
|
260
263
|
logger.warning(
|
|
261
264
|
"The scheduling strategy has been changed to 'round_robin'"
|
|
262
265
|
)
|
|
263
|
-
|
|
264
|
-
|
|
266
|
+
async with self.lock:
|
|
267
|
+
# Skip already used one
|
|
268
|
+
self.current_model = self.scheduling_strategy()
|
|
265
269
|
raise exc
|
|
266
270
|
return response
|
|
@@ -128,12 +128,31 @@ class AutoRetriever:
|
|
|
128
128
|
Returns:
|
|
129
129
|
str: A sanitized, valid collection name suitable for use.
|
|
130
130
|
"""
|
|
131
|
+
import hashlib
|
|
132
|
+
import os
|
|
133
|
+
|
|
131
134
|
from unstructured.documents.elements import Element
|
|
132
135
|
|
|
133
136
|
if isinstance(content, Element):
|
|
134
137
|
content = content.metadata.file_directory or str(uuid.uuid4())
|
|
135
138
|
|
|
136
|
-
|
|
139
|
+
# For file paths, use a combination of directory hash and filename
|
|
140
|
+
if os.path.isfile(content):
|
|
141
|
+
# Get directory and filename
|
|
142
|
+
directory = os.path.dirname(content)
|
|
143
|
+
filename = os.path.basename(content)
|
|
144
|
+
# Create a short hash of the directory path
|
|
145
|
+
dir_hash = hashlib.md5(directory.encode()).hexdigest()[:6]
|
|
146
|
+
# Get filename without extension and remove special chars
|
|
147
|
+
base_name = os.path.splitext(filename)[0]
|
|
148
|
+
clean_name = re.sub(r'[^a-zA-Z0-9]', '', base_name)[:10]
|
|
149
|
+
# Combine for a unique name
|
|
150
|
+
collection_name = f"{clean_name}_{dir_hash}"
|
|
151
|
+
else:
|
|
152
|
+
# For URL content
|
|
153
|
+
content_hash = hashlib.md5(content.encode()).hexdigest()[:6]
|
|
154
|
+
clean_content = re.sub(r'[^a-zA-Z0-9]', '', content)[-10:]
|
|
155
|
+
collection_name = f"{clean_content}_{content_hash}"
|
|
137
156
|
|
|
138
157
|
# Ensure the first character is either an underscore or a letter for
|
|
139
158
|
# Milvus
|
|
@@ -21,7 +21,7 @@ from typing import Any, Dict, List, Optional, Union
|
|
|
21
21
|
from pydantic import BaseModel
|
|
22
22
|
|
|
23
23
|
from camel.logger import get_logger
|
|
24
|
-
from camel.
|
|
24
|
+
from camel.runtimes import BaseRuntime
|
|
25
25
|
from camel.toolkits.function_tool import FunctionTool
|
|
26
26
|
|
|
27
27
|
logger = get_logger(__name__)
|
|
@@ -19,8 +19,8 @@ from typing import List, Optional, Union
|
|
|
19
19
|
from camel.agents import ChatAgent
|
|
20
20
|
from camel.configs import ChatGPTConfig
|
|
21
21
|
from camel.models import BaseModelBackend, ModelFactory
|
|
22
|
-
from camel.
|
|
23
|
-
from camel.
|
|
22
|
+
from camel.runtimes import BaseRuntime
|
|
23
|
+
from camel.runtimes.utils import FunctionRiskToolkit, IgnoreRiskToolkit
|
|
24
24
|
from camel.toolkits import FunctionTool
|
|
25
25
|
from camel.types import ModelPlatformType, ModelType
|
|
26
26
|
|
|
@@ -24,7 +24,7 @@ from typing import Any, Dict, List, Optional, Union
|
|
|
24
24
|
import requests
|
|
25
25
|
from pydantic import BaseModel
|
|
26
26
|
|
|
27
|
-
from camel.
|
|
27
|
+
from camel.runtimes import BaseRuntime
|
|
28
28
|
from camel.toolkits.function_tool import FunctionTool
|
|
29
29
|
|
|
30
30
|
logger = logging.getLogger(__name__)
|
|
@@ -18,7 +18,7 @@ import time
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from typing import Callable, List, Optional, Union
|
|
20
20
|
|
|
21
|
-
from camel.
|
|
21
|
+
from camel.runtimes.docker_runtime import DockerRuntime
|
|
22
22
|
from camel.toolkits import FunctionTool
|
|
23
23
|
|
|
24
24
|
logger = logging.getLogger(__name__)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
from abc import ABC, abstractmethod
|
|
15
|
-
from typing import Any
|
|
15
|
+
from typing import Any, Optional
|
|
16
16
|
|
|
17
17
|
from camel.societies.workforce.task_channel import TaskChannel
|
|
18
18
|
from camel.societies.workforce.utils import check_if_running
|
|
@@ -23,10 +23,14 @@ class BaseNode(ABC):
|
|
|
23
23
|
|
|
24
24
|
Args:
|
|
25
25
|
description (str): Description of the node.
|
|
26
|
+
node_id (Optional[str]): ID of the node. If not provided, it will
|
|
27
|
+
be generated automatically. (default: :obj:`None`)
|
|
26
28
|
"""
|
|
27
29
|
|
|
28
|
-
def __init__(
|
|
29
|
-
self
|
|
30
|
+
def __init__(
|
|
31
|
+
self, description: str, node_id: Optional[str] = None
|
|
32
|
+
) -> None:
|
|
33
|
+
self.node_id = node_id if node_id is not None else str(id(self))
|
|
30
34
|
self.description = description
|
|
31
35
|
self._channel: TaskChannel = TaskChannel()
|
|
32
36
|
self._running = False
|
|
@@ -15,7 +15,7 @@ from __future__ import annotations
|
|
|
15
15
|
|
|
16
16
|
import logging
|
|
17
17
|
from abc import ABC, abstractmethod
|
|
18
|
-
from typing import List
|
|
18
|
+
from typing import List, Optional
|
|
19
19
|
|
|
20
20
|
from colorama import Fore
|
|
21
21
|
|
|
@@ -33,14 +33,16 @@ class Worker(BaseNode, ABC):
|
|
|
33
33
|
|
|
34
34
|
Args:
|
|
35
35
|
description (str): Description of the node.
|
|
36
|
-
|
|
36
|
+
node_id (Optional[str]): ID of the node. If not provided, it will
|
|
37
|
+
be generated automatically. (default: :obj:`None`)
|
|
37
38
|
"""
|
|
38
39
|
|
|
39
40
|
def __init__(
|
|
40
41
|
self,
|
|
41
42
|
description: str,
|
|
43
|
+
node_id: Optional[str] = None,
|
|
42
44
|
) -> None:
|
|
43
|
-
super().__init__(description)
|
|
45
|
+
super().__init__(description, node_id=node_id)
|
|
44
46
|
|
|
45
47
|
def __repr__(self):
|
|
46
48
|
return f"Worker node {self.node_id} ({self.description})"
|