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
camel/__init__.py
CHANGED
camel/agents/chat_agent.py
CHANGED
|
@@ -442,6 +442,11 @@ class ChatAgent(BaseAgent):
|
|
|
442
442
|
new_tool = convert_to_function_tool(tool)
|
|
443
443
|
self._internal_tools[new_tool.get_function_name()] = new_tool
|
|
444
444
|
|
|
445
|
+
def add_tools(self, tools: List[Union[FunctionTool, Callable]]) -> None:
|
|
446
|
+
r"""Add a list of tools to the agent."""
|
|
447
|
+
for tool in tools:
|
|
448
|
+
self.add_tool(tool)
|
|
449
|
+
|
|
445
450
|
def add_external_tool(
|
|
446
451
|
self, tool: Union[FunctionTool, Callable, Dict[str, Any]]
|
|
447
452
|
) -> None:
|
|
@@ -462,6 +467,11 @@ class ChatAgent(BaseAgent):
|
|
|
462
467
|
return True
|
|
463
468
|
return False
|
|
464
469
|
|
|
470
|
+
def remove_tools(self, tool_names: List[str]) -> None:
|
|
471
|
+
r"""Remove a list of tools from the agent by name."""
|
|
472
|
+
for tool_name in tool_names:
|
|
473
|
+
self.remove_tool(tool_name)
|
|
474
|
+
|
|
465
475
|
def remove_external_tool(self, tool_name: str) -> bool:
|
|
466
476
|
r"""Remove an external tool from the agent by name.
|
|
467
477
|
|
|
@@ -1311,7 +1321,9 @@ class ChatAgent(BaseAgent):
|
|
|
1311
1321
|
response_id: str = ""
|
|
1312
1322
|
# All choices in one response share one role
|
|
1313
1323
|
for chunk in response:
|
|
1314
|
-
|
|
1324
|
+
# Some model platforms like siliconflow may return None for the
|
|
1325
|
+
# chunk.id
|
|
1326
|
+
response_id = chunk.id if chunk.id else str(uuid.uuid4())
|
|
1315
1327
|
self._handle_chunk(
|
|
1316
1328
|
chunk, content_dict, finish_reasons_dict, output_messages
|
|
1317
1329
|
)
|
|
@@ -1351,7 +1363,9 @@ class ChatAgent(BaseAgent):
|
|
|
1351
1363
|
response_id: str = ""
|
|
1352
1364
|
# All choices in one response share one role
|
|
1353
1365
|
async for chunk in response:
|
|
1354
|
-
|
|
1366
|
+
# Some model platforms like siliconflow may return None for the
|
|
1367
|
+
# chunk.id
|
|
1368
|
+
response_id = chunk.id if chunk.id else str(uuid.uuid4())
|
|
1355
1369
|
self._handle_chunk(
|
|
1356
1370
|
chunk, content_dict, finish_reasons_dict, output_messages
|
|
1357
1371
|
)
|
camel/agents/mcp_agent.py
CHANGED
|
@@ -12,18 +12,26 @@
|
|
|
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 json
|
|
17
|
+
import platform
|
|
16
18
|
import re
|
|
17
|
-
from typing import Optional
|
|
19
|
+
from typing import Any, Callable, Dict, List, Optional, Union, cast
|
|
18
20
|
|
|
19
21
|
from camel.agents import ChatAgent
|
|
20
22
|
from camel.logger import get_logger
|
|
21
23
|
from camel.messages import BaseMessage
|
|
22
|
-
from camel.models import BaseModelBackend
|
|
24
|
+
from camel.models import BaseModelBackend, ModelFactory
|
|
23
25
|
from camel.prompts import TextPrompt
|
|
24
26
|
from camel.responses import ChatAgentResponse
|
|
25
|
-
from camel.toolkits import MCPToolkit
|
|
26
|
-
from camel.types import
|
|
27
|
+
from camel.toolkits import FunctionTool, MCPToolkit
|
|
28
|
+
from camel.types import (
|
|
29
|
+
BaseMCPRegistryConfig,
|
|
30
|
+
MCPRegistryType,
|
|
31
|
+
ModelPlatformType,
|
|
32
|
+
ModelType,
|
|
33
|
+
RoleType,
|
|
34
|
+
)
|
|
27
35
|
|
|
28
36
|
# AgentOps decorator setting
|
|
29
37
|
try:
|
|
@@ -38,6 +46,7 @@ except (ImportError, AttributeError):
|
|
|
38
46
|
|
|
39
47
|
logger = get_logger(__name__)
|
|
40
48
|
|
|
49
|
+
|
|
41
50
|
SYS_MSG_CONTENT = """
|
|
42
51
|
You are a helpful assistant, and you prefer to use tools provided by the user
|
|
43
52
|
to solve problems.
|
|
@@ -80,22 +89,62 @@ Please answer me according to the result directly.
|
|
|
80
89
|
|
|
81
90
|
@track_agent(name="MCPAgent")
|
|
82
91
|
class MCPAgent(ChatAgent):
|
|
92
|
+
r"""A specialized agent designed to interact with MCP registries.
|
|
93
|
+
The MCPAgent enhances a base ChatAgent by integrating MCP tools from
|
|
94
|
+
various registries for search capabilities.
|
|
95
|
+
|
|
96
|
+
Attributes:
|
|
97
|
+
system_message (Optional[str]): The system message for the chat agent.
|
|
98
|
+
(default: :str:`"You are an assistant with search capabilities
|
|
99
|
+
using MCP tools."`)
|
|
100
|
+
model (BaseModelBackend): The model backend to use for generating
|
|
101
|
+
responses. (default: :obj:`ModelPlatformType.DEFAULT` with
|
|
102
|
+
`ModelType.DEFAULT`)
|
|
103
|
+
registry_configs (List[BaseMCPRegistryConfig]): List of registry
|
|
104
|
+
configurations (default: :obj:`None`)
|
|
105
|
+
local_config (Optional[Dict[str, Any]]): The local configuration for
|
|
106
|
+
the MCP agent. (default: :obj:`None`)
|
|
107
|
+
local_config_path (Optional[str]): The path to the local configuration
|
|
108
|
+
file for the MCP agent. (default: :obj:`None`)
|
|
109
|
+
function_calling_available (bool): Flag indicating whether the
|
|
110
|
+
model is equipped with the function calling ability.
|
|
111
|
+
(default: :obj:`True`)
|
|
112
|
+
**kwargs: Inherited from ChatAgent
|
|
113
|
+
"""
|
|
114
|
+
|
|
83
115
|
def __init__(
|
|
84
116
|
self,
|
|
85
|
-
|
|
117
|
+
system_message: Optional[Union[str, BaseMessage]] = (
|
|
118
|
+
"You are an assistant with search capabilities using MCP tools."
|
|
119
|
+
),
|
|
86
120
|
model: Optional[BaseModelBackend] = None,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
121
|
+
registry_configs: Optional[
|
|
122
|
+
Union[List[BaseMCPRegistryConfig], BaseMCPRegistryConfig]
|
|
123
|
+
] = None,
|
|
124
|
+
local_config: Optional[Dict[str, Any]] = None,
|
|
125
|
+
local_config_path: Optional[str] = None,
|
|
126
|
+
tools: Optional[List[Union[FunctionTool, Callable]]] = None,
|
|
127
|
+
function_calling_available: bool = True,
|
|
128
|
+
**kwargs,
|
|
129
|
+
):
|
|
130
|
+
if model is None:
|
|
131
|
+
model = ModelFactory.create(
|
|
132
|
+
model_platform=ModelPlatformType.DEFAULT,
|
|
133
|
+
model_type=ModelType.DEFAULT,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
if isinstance(registry_configs, BaseMCPRegistryConfig):
|
|
137
|
+
self.registry_configs = [registry_configs]
|
|
138
|
+
else:
|
|
139
|
+
self.registry_configs = registry_configs or []
|
|
140
|
+
|
|
141
|
+
if local_config_path:
|
|
142
|
+
with open(local_config_path, 'r') as f:
|
|
143
|
+
local_config = json.load(f)
|
|
144
|
+
|
|
145
|
+
self.local_config = local_config
|
|
146
|
+
self.function_calling_available = function_calling_available
|
|
90
147
|
|
|
91
|
-
Args:
|
|
92
|
-
config_path (str): Path to the MCP configuration file.
|
|
93
|
-
model (Optional[BaseModelBackend]): Model backend for the agent.
|
|
94
|
-
(default: :obj:`None`)
|
|
95
|
-
function_calling_available (bool): Flag indicating whether the
|
|
96
|
-
model is equipped with the function calling ability.
|
|
97
|
-
(default: :obj:`False`)
|
|
98
|
-
"""
|
|
99
148
|
if function_calling_available:
|
|
100
149
|
sys_msg_content = "You are a helpful assistant, and you prefer "
|
|
101
150
|
"to use tools provided by the user to solve problems."
|
|
@@ -109,63 +158,182 @@ class MCPAgent(ChatAgent):
|
|
|
109
158
|
content=sys_msg_content,
|
|
110
159
|
)
|
|
111
160
|
|
|
112
|
-
|
|
161
|
+
# Initialize the toolkit if configuration is provided
|
|
162
|
+
self.mcp_toolkit = self._initialize_mcp_toolkit()
|
|
163
|
+
|
|
164
|
+
super().__init__(
|
|
165
|
+
system_message=system_message,
|
|
166
|
+
model=model,
|
|
167
|
+
tools=tools,
|
|
168
|
+
**kwargs,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def _initialize_mcp_toolkit(self) -> MCPToolkit:
|
|
172
|
+
r"""Initialize the MCP toolkit from the provided configuration."""
|
|
173
|
+
config_dict = {}
|
|
174
|
+
for registry_config in self.registry_configs:
|
|
175
|
+
config_dict.update(registry_config.get_config())
|
|
113
176
|
|
|
114
|
-
self.
|
|
115
|
-
|
|
116
|
-
self._text_tools = None
|
|
177
|
+
if self.local_config:
|
|
178
|
+
config_dict.update(self.local_config)
|
|
117
179
|
|
|
118
|
-
|
|
119
|
-
r"""Explicitly connect to all MCP servers."""
|
|
120
|
-
await self._mcp_toolkit.connect()
|
|
180
|
+
return MCPToolkit(config_dict=config_dict)
|
|
121
181
|
|
|
122
|
-
|
|
123
|
-
r"""
|
|
124
|
-
await self._mcp_toolkit.disconnect()
|
|
182
|
+
def add_registry(self, registry_config: BaseMCPRegistryConfig) -> None:
|
|
183
|
+
r"""Add a new registry configuration to the agent.
|
|
125
184
|
|
|
126
|
-
|
|
127
|
-
|
|
185
|
+
Args:
|
|
186
|
+
registry_config (BaseMCPRegistryConfig): The registry
|
|
187
|
+
configuration to add.
|
|
188
|
+
"""
|
|
189
|
+
self.registry_configs.append(registry_config)
|
|
190
|
+
# Reinitialize the toolkit with the updated configurations
|
|
191
|
+
self.mcp_toolkit = self._initialize_mcp_toolkit()
|
|
192
|
+
|
|
193
|
+
# If already connected, reconnect to apply changes
|
|
194
|
+
if self.mcp_toolkit and self.mcp_toolkit.is_connected():
|
|
195
|
+
try:
|
|
196
|
+
asyncio.run(self.disconnect())
|
|
197
|
+
asyncio.run(self.connect())
|
|
198
|
+
except RuntimeError as e:
|
|
199
|
+
# Handle case where we're already in an event loop
|
|
200
|
+
logger.warning(
|
|
201
|
+
f"Could not reconnect synchronously: {e}. "
|
|
202
|
+
f"Manual reconnection may be required."
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
@classmethod
|
|
206
|
+
async def create(
|
|
207
|
+
cls,
|
|
208
|
+
config_path: Optional[str] = None,
|
|
209
|
+
registry_configs: Optional[
|
|
210
|
+
Union[List[BaseMCPRegistryConfig], BaseMCPRegistryConfig]
|
|
211
|
+
] = None,
|
|
212
|
+
model: Optional[BaseModelBackend] = None,
|
|
213
|
+
function_calling_available: bool = False,
|
|
214
|
+
**kwargs,
|
|
215
|
+
) -> "MCPAgent":
|
|
216
|
+
r"""Create and connect an MCPAgent instance.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
config_path (Optional[str]): Path to the MCP configuration file.
|
|
220
|
+
If provided, will load registry configs from this file.
|
|
221
|
+
(default: :obj:`None`)
|
|
222
|
+
registry_configs (Optional[Union[List[BaseMCPRegistryConfig],
|
|
223
|
+
BaseMCPRegistryConfig]]): Registry configurations to use.
|
|
224
|
+
Can be a single config or list of configs. If both config_path
|
|
225
|
+
and registry_configs are provided, configs from both sources
|
|
226
|
+
will be combined. (default: :obj:`None`)
|
|
227
|
+
model (Optional[BaseModelBackend]): The model backend to use.
|
|
228
|
+
If None, will use the default model. (default: :obj:`None`)
|
|
229
|
+
function_calling_available (bool): Whether the model supports
|
|
230
|
+
function calling. (default: :obj:`False`)
|
|
231
|
+
**kwargs: Additional arguments to pass to MCPAgent constructor.
|
|
128
232
|
|
|
129
|
-
|
|
130
|
-
|
|
233
|
+
Returns:
|
|
234
|
+
MCPAgent: A connected MCPAgent instance ready to use.
|
|
235
|
+
|
|
236
|
+
Example:
|
|
237
|
+
>>> agent = await MCPAgent.create(
|
|
238
|
+
... config_path="path/to/config.json",
|
|
239
|
+
... function_calling_available=True
|
|
240
|
+
... )
|
|
241
|
+
>>> response = await agent.run("Hello!")
|
|
242
|
+
"""
|
|
243
|
+
# Initialize registry_configs list
|
|
244
|
+
final_registry_configs = []
|
|
131
245
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
246
|
+
# Add configs from registry_configs argument if provided
|
|
247
|
+
if registry_configs is not None:
|
|
248
|
+
if isinstance(registry_configs, BaseMCPRegistryConfig):
|
|
249
|
+
final_registry_configs.append(registry_configs)
|
|
250
|
+
else:
|
|
251
|
+
final_registry_configs.extend(registry_configs)
|
|
252
|
+
|
|
253
|
+
# Load additional configs from file if provided
|
|
254
|
+
if config_path:
|
|
255
|
+
try:
|
|
256
|
+
with open(config_path, 'r') as f:
|
|
257
|
+
config_data = json.load(f)
|
|
258
|
+
|
|
259
|
+
# Create registry configs from the loaded data
|
|
260
|
+
for _, server_config in config_data.get(
|
|
261
|
+
"mcpServers", {}
|
|
262
|
+
).items():
|
|
263
|
+
# Create a custom registry config for each server
|
|
264
|
+
registry_config = BaseMCPRegistryConfig(
|
|
265
|
+
type=MCPRegistryType.CUSTOM,
|
|
266
|
+
os=platform.system().lower(), # type: ignore [arg-type]
|
|
267
|
+
**server_config,
|
|
268
|
+
)
|
|
269
|
+
final_registry_configs.append(registry_config)
|
|
270
|
+
except Exception as e:
|
|
271
|
+
logger.error(f"Failed to load config from {config_path}: {e}")
|
|
272
|
+
raise
|
|
273
|
+
|
|
274
|
+
# Create the agent instance
|
|
275
|
+
agent = cls(
|
|
276
|
+
registry_configs=final_registry_configs,
|
|
277
|
+
model=model,
|
|
278
|
+
function_calling_available=function_calling_available,
|
|
279
|
+
**kwargs,
|
|
135
280
|
)
|
|
136
|
-
if self._function_calling_available:
|
|
137
|
-
tools = self._mcp_toolkit.get_tools()
|
|
138
|
-
for tool in tools:
|
|
139
|
-
self.add_tool(tool)
|
|
140
281
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
282
|
+
# Connect to MCP servers
|
|
283
|
+
try:
|
|
284
|
+
await agent.connect()
|
|
285
|
+
except Exception as e:
|
|
286
|
+
logger.error(f"Failed to connect to MCP servers: {e}")
|
|
287
|
+
await agent.disconnect() # Clean up if connection fails
|
|
288
|
+
raise
|
|
289
|
+
|
|
290
|
+
return agent
|
|
291
|
+
|
|
292
|
+
async def connect(self) -> None:
|
|
293
|
+
r"""Connect to the MCP servers."""
|
|
294
|
+
if self.mcp_toolkit:
|
|
295
|
+
await self.mcp_toolkit.connect()
|
|
296
|
+
if self.function_calling_available:
|
|
297
|
+
self.add_tools(
|
|
298
|
+
cast(
|
|
299
|
+
list[FunctionTool | Callable[..., Any]],
|
|
300
|
+
self.mcp_toolkit.get_tools(),
|
|
301
|
+
)
|
|
302
|
+
)
|
|
303
|
+
else:
|
|
304
|
+
prompt = TextPrompt(TOOLS_PROMPT)
|
|
305
|
+
self._text_tools = prompt.format(
|
|
306
|
+
tools=self.mcp_toolkit.get_text_tools()
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
async def disconnect(self) -> None:
|
|
310
|
+
r"""Disconnect from the MCP servers."""
|
|
311
|
+
if self.mcp_toolkit:
|
|
312
|
+
await self.mcp_toolkit.disconnect()
|
|
313
|
+
|
|
314
|
+
async def astep(
|
|
315
|
+
self, input_message: Union[BaseMessage, str], *args, **kwargs
|
|
144
316
|
) -> ChatAgentResponse:
|
|
145
|
-
r"""
|
|
317
|
+
r"""Asynchronous step function. Make sure MCP toolkit is connected
|
|
318
|
+
before proceeding.
|
|
146
319
|
|
|
147
320
|
Args:
|
|
148
|
-
|
|
149
|
-
|
|
321
|
+
input_message (Union[BaseMessage, str]): The input message.
|
|
322
|
+
*args: Additional arguments.
|
|
323
|
+
**kwargs: Additional keyword arguments.
|
|
150
324
|
|
|
151
325
|
Returns:
|
|
152
|
-
ChatAgentResponse: The
|
|
153
|
-
prompt and potentially executing MCP tool calls.
|
|
154
|
-
|
|
155
|
-
Raises:
|
|
156
|
-
RuntimeError: If the MCP server is not connected when attempting
|
|
157
|
-
to run.
|
|
326
|
+
ChatAgentResponse: The response from the agent.
|
|
158
327
|
"""
|
|
328
|
+
if self.mcp_toolkit and not self.mcp_toolkit.is_connected():
|
|
329
|
+
await self.connect()
|
|
159
330
|
|
|
160
|
-
if
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if self._function_calling_available:
|
|
164
|
-
response = await self.astep(prompt)
|
|
165
|
-
return response
|
|
331
|
+
if self.function_calling_available:
|
|
332
|
+
return await super().astep(input_message, *args, **kwargs)
|
|
166
333
|
else:
|
|
167
|
-
task = f"## Task:\n {
|
|
168
|
-
|
|
334
|
+
task = f"## Task:\n {input_message}"
|
|
335
|
+
input_message = str(self._text_tools) + task
|
|
336
|
+
response = await super().astep(input_message, *args, **kwargs)
|
|
169
337
|
content = response.msgs[0].content.lower()
|
|
170
338
|
|
|
171
339
|
tool_calls = []
|
|
@@ -193,12 +361,45 @@ class MCPAgent(ChatAgent):
|
|
|
193
361
|
else:
|
|
194
362
|
tools_results = []
|
|
195
363
|
for tool_call in tool_calls:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
364
|
+
try:
|
|
365
|
+
server_idx = tool_call.get('server_idx')
|
|
366
|
+
tool_name = tool_call.get('tool_name')
|
|
367
|
+
tool_args = tool_call.get('tool_args', {})
|
|
368
|
+
|
|
369
|
+
# Validate required fields
|
|
370
|
+
if server_idx is None or tool_name is None:
|
|
371
|
+
logger.warning(
|
|
372
|
+
f"Missing required fields in tool "
|
|
373
|
+
f"call: {tool_call}"
|
|
374
|
+
)
|
|
375
|
+
continue
|
|
376
|
+
|
|
377
|
+
# Check server index is valid
|
|
378
|
+
if (
|
|
379
|
+
not isinstance(server_idx, int)
|
|
380
|
+
or server_idx < 0
|
|
381
|
+
or server_idx >= len(self.mcp_toolkit.servers)
|
|
382
|
+
):
|
|
383
|
+
logger.warning(
|
|
384
|
+
f"Invalid server index: {server_idx}"
|
|
385
|
+
)
|
|
386
|
+
continue
|
|
387
|
+
|
|
388
|
+
server = self.mcp_toolkit.servers[server_idx]
|
|
389
|
+
result = await server.call_tool(tool_name, tool_args)
|
|
390
|
+
|
|
391
|
+
# Safely access content
|
|
392
|
+
if result.content and len(result.content) > 0:
|
|
393
|
+
tools_results.append(
|
|
394
|
+
{tool_name: result.content[0].text}
|
|
395
|
+
)
|
|
396
|
+
else:
|
|
397
|
+
tools_results.append(
|
|
398
|
+
{tool_name: "No result content available"}
|
|
399
|
+
)
|
|
400
|
+
except Exception as e:
|
|
401
|
+
logger.error(f"Error processing tool call: {e}")
|
|
402
|
+
tools_results.append({"error": str(e)})
|
|
202
403
|
results = json.dumps(tools_results)
|
|
203
404
|
final_prompt = TextPrompt(FINAL_RESPONSE_PROMPT).format(
|
|
204
405
|
results=results
|
|
@@ -206,28 +407,40 @@ class MCPAgent(ChatAgent):
|
|
|
206
407
|
response = await self.astep(final_prompt)
|
|
207
408
|
return response
|
|
208
409
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
function_calling_available: bool = False,
|
|
215
|
-
) -> "MCPAgent":
|
|
216
|
-
r"""Factory method to create and initialize an MCPAgent.
|
|
410
|
+
def step(
|
|
411
|
+
self, input_message: Union[BaseMessage, str], *args, **kwargs
|
|
412
|
+
) -> ChatAgentResponse:
|
|
413
|
+
r"""Synchronous step function. Make sure MCP toolkit is connected
|
|
414
|
+
before proceeding.
|
|
217
415
|
|
|
218
416
|
Args:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
If None, the default model will be used. (default: :obj:`None`)
|
|
223
|
-
function_calling_available (bool): Flag indicating whether the
|
|
224
|
-
model is equipped with function calling ability. This affects
|
|
225
|
-
the system message content. (default: :obj:`False`)
|
|
417
|
+
input_message (Union[BaseMessage, str]): The input message.
|
|
418
|
+
*args: Additional arguments.
|
|
419
|
+
**kwargs: Additional keyword arguments.
|
|
226
420
|
|
|
227
421
|
Returns:
|
|
228
|
-
|
|
422
|
+
ChatAgentResponse: The response from the agent.
|
|
229
423
|
"""
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
424
|
+
try:
|
|
425
|
+
loop = asyncio.get_running_loop()
|
|
426
|
+
except RuntimeError:
|
|
427
|
+
loop = None
|
|
428
|
+
|
|
429
|
+
if loop and loop.is_running():
|
|
430
|
+
# Running inside an existing loop (e.g., Jupyter/FastAPI)
|
|
431
|
+
# Use create_task and run with a future
|
|
432
|
+
coro = self.astep(input_message, *args, **kwargs)
|
|
433
|
+
future = asyncio.ensure_future(coro)
|
|
434
|
+
return asyncio.run_coroutine_threadsafe(future, loop).result()
|
|
435
|
+
else:
|
|
436
|
+
# Safe to run normally
|
|
437
|
+
return asyncio.run(self.astep(input_message, *args, **kwargs))
|
|
438
|
+
|
|
439
|
+
async def __aenter__(self):
|
|
440
|
+
r"""Async context manager entry."""
|
|
441
|
+
await self.connect()
|
|
442
|
+
return self
|
|
443
|
+
|
|
444
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
445
|
+
r"""Async context manager exit."""
|
|
446
|
+
await self.disconnect()
|
camel/loaders/__init__.py
CHANGED
|
@@ -18,6 +18,7 @@ from .chunkr_reader import ChunkrReader
|
|
|
18
18
|
from .crawl4ai_reader import Crawl4AI
|
|
19
19
|
from .firecrawl_reader import Firecrawl
|
|
20
20
|
from .jina_url_reader import JinaURLReader
|
|
21
|
+
from .markitdown import MarkItDownLoader
|
|
21
22
|
from .mineru_extractor import MinerU
|
|
22
23
|
from .pandas_reader import PandasReader
|
|
23
24
|
from .scrapegraph_reader import ScrapeGraphAI
|
|
@@ -35,5 +36,6 @@ __all__ = [
|
|
|
35
36
|
'PandasReader',
|
|
36
37
|
'MinerU',
|
|
37
38
|
'Crawl4AI',
|
|
39
|
+
'MarkItDownLoader',
|
|
38
40
|
'ScrapeGraphAI',
|
|
39
41
|
]
|