camel-ai 0.2.56__py3-none-any.whl → 0.2.58__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 -2
- camel/agents/chat_agent.py +16 -2
- camel/agents/mcp_agent.py +296 -83
- camel/loaders/__init__.py +2 -0
- camel/loaders/markitdown.py +204 -0
- camel/models/gemini_model.py +10 -0
- camel/models/openai_compatible_model.py +3 -3
- camel/societies/workforce/workforce.py +4 -4
- camel/toolkits/__init__.py +4 -0
- camel/toolkits/async_browser_toolkit.py +1800 -0
- camel/toolkits/base.py +10 -1
- camel/toolkits/browser_toolkit.py +19 -3
- camel/toolkits/excel_toolkit.py +1 -2
- camel/toolkits/function_tool.py +1 -1
- camel/toolkits/mcp_toolkit.py +129 -3
- camel/toolkits/search_toolkit.py +0 -169
- camel/toolkits/wolfram_alpha_toolkit.py +237 -0
- camel/types/__init__.py +10 -0
- camel/types/enums.py +11 -4
- camel/types/mcp_registries.py +157 -0
- {camel_ai-0.2.56.dist-info → camel_ai-0.2.58.dist-info}/METADATA +4 -2
- {camel_ai-0.2.56.dist-info → camel_ai-0.2.58.dist-info}/RECORD +24 -20
- {camel_ai-0.2.56.dist-info → camel_ai-0.2.58.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.56.dist-info → camel_ai-0.2.58.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,204 @@
|
|
|
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 concurrent.futures import ThreadPoolExecutor, as_completed
|
|
16
|
+
from typing import ClassVar, Dict, List, Optional
|
|
17
|
+
|
|
18
|
+
from camel.logger import get_logger
|
|
19
|
+
|
|
20
|
+
logger = get_logger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MarkItDownLoader:
|
|
24
|
+
r"""MarkitDown convert various file types into Markdown format.
|
|
25
|
+
|
|
26
|
+
Supported Input Formats:
|
|
27
|
+
- PDF
|
|
28
|
+
- Microsoft Office documents:
|
|
29
|
+
- Word (.doc, .docx)
|
|
30
|
+
- Excel (.xls, .xlsx)
|
|
31
|
+
- PowerPoint (.ppt, .pptx)
|
|
32
|
+
- EPUB
|
|
33
|
+
- HTML
|
|
34
|
+
- Images (with EXIF metadata and OCR support)
|
|
35
|
+
- Audio files (with EXIF metadata and speech transcription)
|
|
36
|
+
- Text-based formats:
|
|
37
|
+
- CSV
|
|
38
|
+
- JSON
|
|
39
|
+
- XML
|
|
40
|
+
- ZIP archives (iterates over contents)
|
|
41
|
+
- YouTube URLs (via transcript extraction)
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
SUPPORTED_FORMATS: ClassVar[List[str]] = [
|
|
45
|
+
".pdf",
|
|
46
|
+
".doc",
|
|
47
|
+
".docx",
|
|
48
|
+
".xls",
|
|
49
|
+
".xlsx",
|
|
50
|
+
".ppt",
|
|
51
|
+
".pptx",
|
|
52
|
+
".epub",
|
|
53
|
+
".html",
|
|
54
|
+
".htm",
|
|
55
|
+
".jpg",
|
|
56
|
+
".jpeg",
|
|
57
|
+
".png",
|
|
58
|
+
".mp3",
|
|
59
|
+
".wav",
|
|
60
|
+
".csv",
|
|
61
|
+
".json",
|
|
62
|
+
".xml",
|
|
63
|
+
".zip",
|
|
64
|
+
".txt",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
llm_client: Optional[object] = None,
|
|
70
|
+
llm_model: Optional[str] = None,
|
|
71
|
+
):
|
|
72
|
+
r"""Initializes the Converter.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
llm_client (Optional[object]): Optional client for LLM integration.
|
|
76
|
+
(default: :obj:`None`)
|
|
77
|
+
llm_model (Optional[str]): Optional model name for the LLM.
|
|
78
|
+
(default: :obj:`None`)
|
|
79
|
+
"""
|
|
80
|
+
from markitdown import MarkItDown
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
self.converter = MarkItDown(
|
|
84
|
+
llm_client=llm_client, llm_model=llm_model
|
|
85
|
+
)
|
|
86
|
+
logger.info("MarkItDownLoader initialized successfully.")
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.error(f"Failed to initialize MarkItDown Converter: {e}")
|
|
89
|
+
raise Exception(f"Failed to initialize MarkItDown Converter: {e}")
|
|
90
|
+
|
|
91
|
+
def _validate_format(self, file_path: str) -> bool:
|
|
92
|
+
r"""Validates if the file format is supported.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
file_path (str): Path to the input file.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
bool: True if the format is supported, False otherwise.
|
|
99
|
+
"""
|
|
100
|
+
_, ext = os.path.splitext(file_path)
|
|
101
|
+
return ext.lower() in self.SUPPORTED_FORMATS
|
|
102
|
+
|
|
103
|
+
def convert_file(self, file_path: str) -> str:
|
|
104
|
+
r"""Converts the given file to Markdown format.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
file_path (str): Path to the input file.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
str: Converted Markdown text.
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
FileNotFoundError: If the specified file does not exist.
|
|
114
|
+
ValueError: If the file format is not supported.
|
|
115
|
+
Exception: For other errors during conversion.
|
|
116
|
+
"""
|
|
117
|
+
if not os.path.isfile(file_path):
|
|
118
|
+
logger.error(f"File not found: {file_path}")
|
|
119
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
120
|
+
|
|
121
|
+
if not self._validate_format(file_path):
|
|
122
|
+
logger.error(
|
|
123
|
+
f"Unsupported file format: {file_path}."
|
|
124
|
+
f"Supported formats are "
|
|
125
|
+
f"{MarkItDownLoader.SUPPORTED_FORMATS}"
|
|
126
|
+
)
|
|
127
|
+
raise ValueError(f"Unsupported file format: {file_path}")
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
logger.info(f"Converting file: {file_path}")
|
|
131
|
+
result = self.converter.convert(file_path)
|
|
132
|
+
logger.info(f"File converted successfully: {file_path}")
|
|
133
|
+
return result.text_content
|
|
134
|
+
except Exception as e:
|
|
135
|
+
logger.error(f"Error converting file '{file_path}': {e}")
|
|
136
|
+
raise Exception(f"Error converting file '{file_path}': {e}")
|
|
137
|
+
|
|
138
|
+
def convert_files(
|
|
139
|
+
self,
|
|
140
|
+
file_paths: List[str],
|
|
141
|
+
parallel: bool = False,
|
|
142
|
+
skip_failed: bool = False,
|
|
143
|
+
) -> Dict[str, str]:
|
|
144
|
+
r"""Converts multiple files to Markdown format.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
file_paths (List[str]): List of file paths to convert.
|
|
148
|
+
parallel (bool): Whether to process files in parallel.
|
|
149
|
+
(default: :obj:`False`)
|
|
150
|
+
skip_failed (bool): Whether to skip failed files instead
|
|
151
|
+
of including error messages.
|
|
152
|
+
(default: :obj:`False`)
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Dict[str, str]: Dictionary mapping file paths to their
|
|
156
|
+
converted Markdown text.
|
|
157
|
+
|
|
158
|
+
Raises:
|
|
159
|
+
Exception: For errors during conversion of any file if
|
|
160
|
+
skip_failed is False.
|
|
161
|
+
"""
|
|
162
|
+
from tqdm.auto import tqdm
|
|
163
|
+
|
|
164
|
+
converted_files = {}
|
|
165
|
+
|
|
166
|
+
if parallel:
|
|
167
|
+
with ThreadPoolExecutor() as executor:
|
|
168
|
+
future_to_path = {
|
|
169
|
+
executor.submit(self.convert_file, path): path
|
|
170
|
+
for path in file_paths
|
|
171
|
+
}
|
|
172
|
+
for future in tqdm(
|
|
173
|
+
as_completed(future_to_path),
|
|
174
|
+
total=len(file_paths),
|
|
175
|
+
desc="Converting files (parallel)",
|
|
176
|
+
):
|
|
177
|
+
path = future_to_path[future]
|
|
178
|
+
try:
|
|
179
|
+
converted_files[path] = future.result()
|
|
180
|
+
except Exception as e:
|
|
181
|
+
if skip_failed:
|
|
182
|
+
logger.warning(
|
|
183
|
+
f"Skipping file '{path}' due to error: {e}"
|
|
184
|
+
)
|
|
185
|
+
else:
|
|
186
|
+
logger.error(
|
|
187
|
+
f"Error processing file '{path}': {e}"
|
|
188
|
+
)
|
|
189
|
+
converted_files[path] = f"Error: {e}"
|
|
190
|
+
else:
|
|
191
|
+
for path in tqdm(file_paths, desc="Converting files (sequential)"):
|
|
192
|
+
try:
|
|
193
|
+
logger.info(f"Processing file: {path}")
|
|
194
|
+
converted_files[path] = self.convert_file(path)
|
|
195
|
+
except Exception as e:
|
|
196
|
+
if skip_failed:
|
|
197
|
+
logger.warning(
|
|
198
|
+
f"Skipping file '{path}' due to error: {e}"
|
|
199
|
+
)
|
|
200
|
+
else:
|
|
201
|
+
logger.error(f"Error processing file '{path}': {e}")
|
|
202
|
+
converted_files[path] = f"Error: {e}"
|
|
203
|
+
|
|
204
|
+
return converted_files
|
camel/models/gemini_model.py
CHANGED
|
@@ -125,6 +125,11 @@ class GeminiModel(OpenAICompatibleModel):
|
|
|
125
125
|
)
|
|
126
126
|
messages = self._process_messages(messages)
|
|
127
127
|
if response_format:
|
|
128
|
+
if tools:
|
|
129
|
+
raise ValueError(
|
|
130
|
+
"Gemini does not support function calling with "
|
|
131
|
+
"response format."
|
|
132
|
+
)
|
|
128
133
|
return self._request_parse(messages, response_format)
|
|
129
134
|
else:
|
|
130
135
|
return self._request_chat_completion(messages, tools)
|
|
@@ -155,6 +160,11 @@ class GeminiModel(OpenAICompatibleModel):
|
|
|
155
160
|
)
|
|
156
161
|
messages = self._process_messages(messages)
|
|
157
162
|
if response_format:
|
|
163
|
+
if tools:
|
|
164
|
+
raise ValueError(
|
|
165
|
+
"Gemini does not support function calling with "
|
|
166
|
+
"response format."
|
|
167
|
+
)
|
|
158
168
|
return await self._arequest_parse(messages, response_format)
|
|
159
169
|
else:
|
|
160
170
|
return await self._arequest_chat_completion(messages, tools)
|
|
@@ -16,7 +16,7 @@ import os
|
|
|
16
16
|
from json import JSONDecodeError
|
|
17
17
|
from typing import Any, Dict, List, Optional, Type, Union
|
|
18
18
|
|
|
19
|
-
from openai import AsyncOpenAI, AsyncStream, OpenAI, Stream
|
|
19
|
+
from openai import AsyncOpenAI, AsyncStream, BadRequestError, OpenAI, Stream
|
|
20
20
|
from pydantic import BaseModel, ValidationError
|
|
21
21
|
|
|
22
22
|
from camel.logger import get_logger
|
|
@@ -198,7 +198,7 @@ class OpenAICompatibleModel(BaseModelBackend):
|
|
|
198
198
|
model=self.model_type,
|
|
199
199
|
**request_config,
|
|
200
200
|
)
|
|
201
|
-
except (ValidationError, JSONDecodeError) as e:
|
|
201
|
+
except (ValidationError, JSONDecodeError, BadRequestError) as e:
|
|
202
202
|
logger.warning(
|
|
203
203
|
f"Format validation error: {e}. "
|
|
204
204
|
f"Attempting fallback with JSON format."
|
|
@@ -237,7 +237,7 @@ class OpenAICompatibleModel(BaseModelBackend):
|
|
|
237
237
|
model=self.model_type,
|
|
238
238
|
**request_config,
|
|
239
239
|
)
|
|
240
|
-
except (ValidationError, JSONDecodeError) as e:
|
|
240
|
+
except (ValidationError, JSONDecodeError, BadRequestError) as e:
|
|
241
241
|
logger.warning(
|
|
242
242
|
f"Format validation error: {e}. "
|
|
243
243
|
f"Attempting fallback with JSON format."
|
|
@@ -41,7 +41,7 @@ from camel.societies.workforce.utils import (
|
|
|
41
41
|
)
|
|
42
42
|
from camel.societies.workforce.worker import Worker
|
|
43
43
|
from camel.tasks.task import Task, TaskState
|
|
44
|
-
from camel.toolkits import
|
|
44
|
+
from camel.toolkits import CodeExecutionToolkit, SearchToolkit, ThinkingToolkit
|
|
45
45
|
from camel.types import ModelPlatformType, ModelType
|
|
46
46
|
|
|
47
47
|
logger = get_logger(__name__)
|
|
@@ -370,9 +370,9 @@ class Workforce(BaseNode):
|
|
|
370
370
|
|
|
371
371
|
# Default tools for a new agent
|
|
372
372
|
function_list = [
|
|
373
|
-
|
|
374
|
-
*
|
|
375
|
-
*
|
|
373
|
+
SearchToolkit().search_duckduckgo,
|
|
374
|
+
*CodeExecutionToolkit().get_tools(),
|
|
375
|
+
*ThinkingToolkit().get_tools(),
|
|
376
376
|
]
|
|
377
377
|
|
|
378
378
|
model_config_dict = ChatGPTConfig(
|
camel/toolkits/__init__.py
CHANGED
|
@@ -59,6 +59,7 @@ from .video_analysis_toolkit import VideoAnalysisToolkit
|
|
|
59
59
|
from .image_analysis_toolkit import ImageAnalysisToolkit
|
|
60
60
|
from .mcp_toolkit import MCPToolkit
|
|
61
61
|
from .browser_toolkit import BrowserToolkit
|
|
62
|
+
from .async_browser_toolkit import AsyncBrowserToolkit
|
|
62
63
|
from .file_write_toolkit import FileWriteToolkit
|
|
63
64
|
from .terminal_toolkit import TerminalToolkit
|
|
64
65
|
from .pubmed_toolkit import PubMedToolkit
|
|
@@ -72,6 +73,7 @@ from .pulse_mcp_search_toolkit import PulseMCPSearchToolkit
|
|
|
72
73
|
from .klavis_toolkit import KlavisToolkit
|
|
73
74
|
from .aci_toolkit import ACIToolkit
|
|
74
75
|
from .playwright_mcp_toolkit import PlaywrightMCPToolkit
|
|
76
|
+
from .wolfram_alpha_toolkit import WolframAlphaToolkit
|
|
75
77
|
|
|
76
78
|
|
|
77
79
|
__all__ = [
|
|
@@ -119,6 +121,7 @@ __all__ = [
|
|
|
119
121
|
'VideoAnalysisToolkit',
|
|
120
122
|
'ImageAnalysisToolkit',
|
|
121
123
|
'BrowserToolkit',
|
|
124
|
+
'AsyncBrowserToolkit',
|
|
122
125
|
'FileWriteToolkit',
|
|
123
126
|
'TerminalToolkit',
|
|
124
127
|
'PubMedToolkit',
|
|
@@ -132,4 +135,5 @@ __all__ = [
|
|
|
132
135
|
'KlavisToolkit',
|
|
133
136
|
'ACIToolkit',
|
|
134
137
|
'PlaywrightMCPToolkit',
|
|
138
|
+
'WolframAlphaToolkit',
|
|
135
139
|
]
|