camel-ai 0.2.54__py3-none-any.whl → 0.2.56__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 +18 -12
- camel/models/mistral_model.py +35 -1
- camel/models/model_factory.py +45 -73
- camel/toolkits/__init__.py +4 -0
- camel/toolkits/klavis_toolkit.py +57 -13
- camel/toolkits/mcp_toolkit.py +55 -16
- camel/toolkits/playwright_mcp_toolkit.py +69 -0
- camel/toolkits/pulse_mcp_search_toolkit.py +132 -0
- camel/types/enums.py +19 -0
- camel/utils/__init__.py +2 -1
- camel/utils/response_format.py +84 -2
- {camel_ai-0.2.54.dist-info → camel_ai-0.2.56.dist-info}/METADATA +1 -1
- {camel_ai-0.2.54.dist-info → camel_ai-0.2.56.dist-info}/RECORD +16 -14
- {camel_ai-0.2.54.dist-info → camel_ai-0.2.56.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.54.dist-info → camel_ai-0.2.56.dist-info}/licenses/LICENSE +0 -0
camel/__init__.py
CHANGED
camel/agents/chat_agent.py
CHANGED
|
@@ -752,7 +752,7 @@ class ChatAgent(BaseAgent):
|
|
|
752
752
|
try:
|
|
753
753
|
openai_messages, num_tokens = self.memory.get_context()
|
|
754
754
|
except RuntimeError as e:
|
|
755
|
-
return self.
|
|
755
|
+
return self._step_terminate(
|
|
756
756
|
e.args[1], tool_call_records, "max_tokens_exceeded"
|
|
757
757
|
)
|
|
758
758
|
# Get response from model backend
|
|
@@ -765,8 +765,8 @@ class ChatAgent(BaseAgent):
|
|
|
765
765
|
|
|
766
766
|
# Terminate Agent if stop_event is set
|
|
767
767
|
if self.stop_event and self.stop_event.is_set():
|
|
768
|
-
# Use the
|
|
769
|
-
return self.
|
|
768
|
+
# Use the _step_terminate to terminate the agent with reason
|
|
769
|
+
return self._step_terminate(
|
|
770
770
|
num_tokens, tool_call_records, "termination_triggered"
|
|
771
771
|
)
|
|
772
772
|
|
|
@@ -851,7 +851,7 @@ class ChatAgent(BaseAgent):
|
|
|
851
851
|
try:
|
|
852
852
|
openai_messages, num_tokens = self.memory.get_context()
|
|
853
853
|
except RuntimeError as e:
|
|
854
|
-
return self.
|
|
854
|
+
return self._step_terminate(
|
|
855
855
|
e.args[1], tool_call_records, "max_tokens_exceeded"
|
|
856
856
|
)
|
|
857
857
|
|
|
@@ -864,8 +864,8 @@ class ChatAgent(BaseAgent):
|
|
|
864
864
|
|
|
865
865
|
# Terminate Agent if stop_event is set
|
|
866
866
|
if self.stop_event and self.stop_event.is_set():
|
|
867
|
-
# Use the
|
|
868
|
-
return self.
|
|
867
|
+
# Use the _step_terminate to terminate the agent with reason
|
|
868
|
+
return self._step_terminate(
|
|
869
869
|
num_tokens, tool_call_records, "termination_triggered"
|
|
870
870
|
)
|
|
871
871
|
|
|
@@ -1396,24 +1396,30 @@ class ChatAgent(BaseAgent):
|
|
|
1396
1396
|
)
|
|
1397
1397
|
output_messages.append(chat_message)
|
|
1398
1398
|
|
|
1399
|
-
def
|
|
1399
|
+
def _step_terminate(
|
|
1400
1400
|
self,
|
|
1401
1401
|
num_tokens: int,
|
|
1402
1402
|
tool_calls: List[ToolCallingRecord],
|
|
1403
1403
|
termination_reason: str,
|
|
1404
1404
|
) -> ChatAgentResponse:
|
|
1405
|
-
r"""
|
|
1406
|
-
|
|
1405
|
+
r"""Create a response when the agent execution is terminated.
|
|
1406
|
+
|
|
1407
|
+
This method is called when the agent needs to terminate its execution
|
|
1408
|
+
due to various reasons such as token limit exceeded, or other
|
|
1409
|
+
termination conditions. It creates a response with empty messages but
|
|
1410
|
+
includes termination information in the info dictionary.
|
|
1407
1411
|
|
|
1408
1412
|
Args:
|
|
1409
1413
|
num_tokens (int): Number of tokens in the messages.
|
|
1410
1414
|
tool_calls (List[ToolCallingRecord]): List of information
|
|
1411
1415
|
objects of functions called in the current step.
|
|
1412
|
-
termination_reason (str): String
|
|
1416
|
+
termination_reason (str): String describing the reason for
|
|
1417
|
+
termination.
|
|
1413
1418
|
|
|
1414
1419
|
Returns:
|
|
1415
|
-
ChatAgentResponse:
|
|
1416
|
-
|
|
1420
|
+
ChatAgentResponse: A response object with empty message list,
|
|
1421
|
+
terminated flag set to True, and an info dictionary containing
|
|
1422
|
+
termination details, token counts, and tool call information.
|
|
1417
1423
|
"""
|
|
1418
1424
|
self.terminated = True
|
|
1419
1425
|
|
camel/models/mistral_model.py
CHANGED
|
@@ -25,6 +25,7 @@ if TYPE_CHECKING:
|
|
|
25
25
|
from openai import AsyncStream
|
|
26
26
|
|
|
27
27
|
from camel.configs import MISTRAL_API_PARAMS, MistralConfig
|
|
28
|
+
from camel.logger import get_logger
|
|
28
29
|
from camel.messages import OpenAIMessage
|
|
29
30
|
from camel.models import BaseModelBackend
|
|
30
31
|
from camel.models._utils import try_modify_message_with_format
|
|
@@ -36,6 +37,8 @@ from camel.utils import (
|
|
|
36
37
|
dependencies_required,
|
|
37
38
|
)
|
|
38
39
|
|
|
40
|
+
logger = get_logger(__name__)
|
|
41
|
+
|
|
39
42
|
try:
|
|
40
43
|
if os.getenv("AGENTOPS_API_KEY") is not None:
|
|
41
44
|
from agentops import LLMEvent, record
|
|
@@ -235,7 +238,38 @@ class MistralModel(BaseModelBackend):
|
|
|
235
238
|
response_format: Optional[Type[BaseModel]] = None,
|
|
236
239
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
237
240
|
) -> Union[ChatCompletion, AsyncStream[ChatCompletionChunk]]:
|
|
238
|
-
|
|
241
|
+
logger.warning(
|
|
242
|
+
"Mistral does not support async inference, using sync "
|
|
243
|
+
"inference instead."
|
|
244
|
+
)
|
|
245
|
+
request_config = self._prepare_request(
|
|
246
|
+
messages, response_format, tools
|
|
247
|
+
)
|
|
248
|
+
mistral_messages = self._to_mistral_chatmessage(messages)
|
|
249
|
+
|
|
250
|
+
response = self._client.chat.complete(
|
|
251
|
+
messages=mistral_messages,
|
|
252
|
+
model=self.model_type,
|
|
253
|
+
**request_config,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
openai_response = self._to_openai_response(response) # type: ignore[arg-type]
|
|
257
|
+
|
|
258
|
+
# Add AgentOps LLM Event tracking
|
|
259
|
+
if LLMEvent:
|
|
260
|
+
llm_event = LLMEvent(
|
|
261
|
+
thread_id=openai_response.id,
|
|
262
|
+
prompt=" ".join(
|
|
263
|
+
[message.get("content") for message in messages] # type: ignore[misc]
|
|
264
|
+
),
|
|
265
|
+
prompt_tokens=openai_response.usage.prompt_tokens, # type: ignore[union-attr]
|
|
266
|
+
completion=openai_response.choices[0].message.content,
|
|
267
|
+
completion_tokens=openai_response.usage.completion_tokens, # type: ignore[union-attr]
|
|
268
|
+
model=self.model_type,
|
|
269
|
+
)
|
|
270
|
+
record(llm_event)
|
|
271
|
+
|
|
272
|
+
return openai_response
|
|
239
273
|
|
|
240
274
|
def _run(
|
|
241
275
|
self,
|
camel/models/model_factory.py
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
import json
|
|
15
|
-
from typing import Dict, Optional, Type, Union
|
|
15
|
+
from typing import ClassVar, Dict, Optional, Type, Union
|
|
16
16
|
|
|
17
17
|
import yaml
|
|
18
18
|
|
|
@@ -62,6 +62,44 @@ class ModelFactory:
|
|
|
62
62
|
ValueError: in case the provided model type is unknown.
|
|
63
63
|
"""
|
|
64
64
|
|
|
65
|
+
_MODEL_PLATFORM_TO_CLASS_MAP: ClassVar[
|
|
66
|
+
Dict[ModelPlatformType, Type[BaseModelBackend]]
|
|
67
|
+
] = {
|
|
68
|
+
ModelPlatformType.OLLAMA: OllamaModel,
|
|
69
|
+
ModelPlatformType.VLLM: VLLMModel,
|
|
70
|
+
ModelPlatformType.SGLANG: SGLangModel,
|
|
71
|
+
ModelPlatformType.OPENAI_COMPATIBLE_MODEL: OpenAICompatibleModel,
|
|
72
|
+
ModelPlatformType.SAMBA: SambaModel,
|
|
73
|
+
ModelPlatformType.TOGETHER: TogetherAIModel,
|
|
74
|
+
ModelPlatformType.LITELLM: LiteLLMModel,
|
|
75
|
+
ModelPlatformType.AWS_BEDROCK: AWSBedrockModel,
|
|
76
|
+
ModelPlatformType.NVIDIA: NvidiaModel,
|
|
77
|
+
ModelPlatformType.SILICONFLOW: SiliconFlowModel,
|
|
78
|
+
ModelPlatformType.AIML: AIMLModel,
|
|
79
|
+
ModelPlatformType.VOLCANO: VolcanoModel,
|
|
80
|
+
ModelPlatformType.NETMIND: NetmindModel,
|
|
81
|
+
ModelPlatformType.OPENAI: OpenAIModel,
|
|
82
|
+
ModelPlatformType.AZURE: AzureOpenAIModel,
|
|
83
|
+
ModelPlatformType.ANTHROPIC: AnthropicModel,
|
|
84
|
+
ModelPlatformType.GROQ: GroqModel,
|
|
85
|
+
ModelPlatformType.LMSTUDIO: LMStudioModel,
|
|
86
|
+
ModelPlatformType.OPENROUTER: OpenRouterModel,
|
|
87
|
+
ModelPlatformType.ZHIPU: ZhipuAIModel,
|
|
88
|
+
ModelPlatformType.GEMINI: GeminiModel,
|
|
89
|
+
ModelPlatformType.MISTRAL: MistralModel,
|
|
90
|
+
ModelPlatformType.REKA: RekaModel,
|
|
91
|
+
ModelPlatformType.COHERE: CohereModel,
|
|
92
|
+
ModelPlatformType.YI: YiModel,
|
|
93
|
+
ModelPlatformType.QWEN: QwenModel,
|
|
94
|
+
ModelPlatformType.DEEPSEEK: DeepSeekModel,
|
|
95
|
+
ModelPlatformType.PPIO: PPIOModel,
|
|
96
|
+
ModelPlatformType.INTERNLM: InternLMModel,
|
|
97
|
+
ModelPlatformType.MOONSHOT: MoonshotModel,
|
|
98
|
+
ModelPlatformType.MODELSCOPE: ModelScopeModel,
|
|
99
|
+
ModelPlatformType.NOVITA: NovitaModel,
|
|
100
|
+
ModelPlatformType.WATSONX: WatsonXModel,
|
|
101
|
+
}
|
|
102
|
+
|
|
65
103
|
@staticmethod
|
|
66
104
|
def create(
|
|
67
105
|
model_platform: Union[ModelPlatformType, str],
|
|
@@ -124,81 +162,15 @@ class ModelFactory:
|
|
|
124
162
|
model_class: Optional[Type[BaseModelBackend]] = None
|
|
125
163
|
model_type = UnifiedModelType(model_type)
|
|
126
164
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
model_class = SGLangModel
|
|
133
|
-
elif model_platform.is_openai_compatible_model:
|
|
134
|
-
model_class = OpenAICompatibleModel
|
|
135
|
-
elif model_platform.is_samba:
|
|
136
|
-
model_class = SambaModel
|
|
137
|
-
elif model_platform.is_together:
|
|
138
|
-
model_class = TogetherAIModel
|
|
139
|
-
elif model_platform.is_litellm:
|
|
140
|
-
model_class = LiteLLMModel
|
|
141
|
-
elif model_platform.is_aws_bedrock:
|
|
142
|
-
model_class = AWSBedrockModel
|
|
143
|
-
elif model_platform.is_nvidia:
|
|
144
|
-
model_class = NvidiaModel
|
|
145
|
-
elif model_platform.is_siliconflow:
|
|
146
|
-
model_class = SiliconFlowModel
|
|
147
|
-
elif model_platform.is_aiml:
|
|
148
|
-
model_class = AIMLModel
|
|
149
|
-
elif model_platform.is_volcano:
|
|
150
|
-
model_class = VolcanoModel
|
|
151
|
-
elif model_platform.is_netmind:
|
|
152
|
-
model_class = NetmindModel
|
|
153
|
-
|
|
154
|
-
elif model_platform.is_openai and model_type.is_openai:
|
|
155
|
-
model_class = OpenAIModel
|
|
156
|
-
elif model_platform.is_azure and model_type.is_azure_openai:
|
|
157
|
-
model_class = AzureOpenAIModel
|
|
158
|
-
elif model_platform.is_anthropic and model_type.is_anthropic:
|
|
159
|
-
model_class = AnthropicModel
|
|
160
|
-
elif model_platform.is_groq and model_type.is_groq:
|
|
161
|
-
model_class = GroqModel
|
|
162
|
-
elif model_platform.is_lmstudio and model_type.is_lmstudio:
|
|
163
|
-
model_class = LMStudioModel
|
|
164
|
-
elif model_platform.is_openrouter and model_type.is_openrouter:
|
|
165
|
-
model_class = OpenRouterModel
|
|
166
|
-
elif model_platform.is_zhipuai and model_type.is_zhipuai:
|
|
167
|
-
model_class = ZhipuAIModel
|
|
168
|
-
elif model_platform.is_gemini and model_type.is_gemini:
|
|
169
|
-
model_class = GeminiModel
|
|
170
|
-
elif model_platform.is_mistral and model_type.is_mistral:
|
|
171
|
-
model_class = MistralModel
|
|
172
|
-
elif model_platform.is_reka and model_type.is_reka:
|
|
173
|
-
model_class = RekaModel
|
|
174
|
-
elif model_platform.is_cohere and model_type.is_cohere:
|
|
175
|
-
model_class = CohereModel
|
|
176
|
-
elif model_platform.is_yi and model_type.is_yi:
|
|
177
|
-
model_class = YiModel
|
|
178
|
-
elif model_platform.is_qwen and model_type.is_qwen:
|
|
179
|
-
model_class = QwenModel
|
|
180
|
-
elif model_platform.is_deepseek:
|
|
181
|
-
model_class = DeepSeekModel
|
|
182
|
-
elif model_platform.is_ppio:
|
|
183
|
-
model_class = PPIOModel
|
|
184
|
-
elif model_platform.is_internlm and model_type.is_internlm:
|
|
185
|
-
model_class = InternLMModel
|
|
186
|
-
elif model_platform.is_moonshot and model_type.is_moonshot:
|
|
187
|
-
model_class = MoonshotModel
|
|
188
|
-
elif model_platform.is_modelscope:
|
|
189
|
-
model_class = ModelScopeModel
|
|
190
|
-
elif model_platform.is_novita:
|
|
191
|
-
model_class = NovitaModel
|
|
192
|
-
elif model_type == ModelType.STUB:
|
|
165
|
+
model_class = ModelFactory._MODEL_PLATFORM_TO_CLASS_MAP.get(
|
|
166
|
+
model_platform
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
if model_type == ModelType.STUB:
|
|
193
170
|
model_class = StubModel
|
|
194
|
-
elif model_type.is_watsonx:
|
|
195
|
-
model_class = WatsonXModel
|
|
196
171
|
|
|
197
172
|
if model_class is None:
|
|
198
|
-
raise ValueError(
|
|
199
|
-
f"Unknown pair of model platform `{model_platform}` "
|
|
200
|
-
f"and model type `{model_type}`."
|
|
201
|
-
)
|
|
173
|
+
raise ValueError(f"Unknown model platform `{model_platform}`")
|
|
202
174
|
|
|
203
175
|
return model_class(
|
|
204
176
|
model_type=model_type,
|
camel/toolkits/__init__.py
CHANGED
|
@@ -68,8 +68,10 @@ 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 .pulse_mcp_search_toolkit import PulseMCPSearchToolkit
|
|
71
72
|
from .klavis_toolkit import KlavisToolkit
|
|
72
73
|
from .aci_toolkit import ACIToolkit
|
|
74
|
+
from .playwright_mcp_toolkit import PlaywrightMCPToolkit
|
|
73
75
|
|
|
74
76
|
|
|
75
77
|
__all__ = [
|
|
@@ -126,6 +128,8 @@ __all__ = [
|
|
|
126
128
|
'OpenAIAgentToolkit',
|
|
127
129
|
'SearxNGToolkit',
|
|
128
130
|
'JinaRerankerToolkit',
|
|
131
|
+
'PulseMCPSearchToolkit',
|
|
129
132
|
'KlavisToolkit',
|
|
130
133
|
'ACIToolkit',
|
|
134
|
+
'PlaywrightMCPToolkit',
|
|
131
135
|
]
|
camel/toolkits/klavis_toolkit.py
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
|
|
15
15
|
import os
|
|
16
|
+
import urllib.parse
|
|
16
17
|
from typing import Any, Dict, List, Optional
|
|
17
18
|
|
|
18
19
|
import requests
|
|
@@ -169,18 +170,6 @@ class KlavisToolkit(BaseToolkit):
|
|
|
169
170
|
endpoint = f"/mcp-server/instance/delete/{instance_id}"
|
|
170
171
|
return self._request('DELETE', endpoint)
|
|
171
172
|
|
|
172
|
-
def get_server_tools(self, server_name: str) -> Dict[str, Any]:
|
|
173
|
-
r"""Get list of tool names for a specific MCP server.
|
|
174
|
-
|
|
175
|
-
Args:
|
|
176
|
-
server_name (str): The name of the target MCP server.
|
|
177
|
-
|
|
178
|
-
Returns:
|
|
179
|
-
Dict[str, Any]: List of tools available for the specified server.
|
|
180
|
-
"""
|
|
181
|
-
endpoint = f"/mcp-server/tools/{server_name}"
|
|
182
|
-
return self._request('GET', endpoint)
|
|
183
|
-
|
|
184
173
|
def get_all_servers(self) -> Dict[str, Any]:
|
|
185
174
|
r"""Get all MCP servers with their basic information.
|
|
186
175
|
|
|
@@ -209,6 +198,60 @@ class KlavisToolkit(BaseToolkit):
|
|
|
209
198
|
'POST', endpoint, payload=payload, additional_headers=headers
|
|
210
199
|
)
|
|
211
200
|
|
|
201
|
+
def list_tools(self, server_url: str) -> Dict[str, Any]:
|
|
202
|
+
r"""Lists all tools available for a specific remote MCP server.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
server_url (str): The full URL for connecting to the MCP server
|
|
206
|
+
via Server-Sent Events (SSE).
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Dict[str, Any]: Response containing the list of tools or an error.
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
encoded_server_url = urllib.parse.quote(server_url, safe='')
|
|
213
|
+
endpoint = f"/mcp-server/list-tools/{encoded_server_url}"
|
|
214
|
+
return self._request('GET', endpoint)
|
|
215
|
+
|
|
216
|
+
def call_tool(
|
|
217
|
+
self,
|
|
218
|
+
server_url: str,
|
|
219
|
+
tool_name: str,
|
|
220
|
+
tool_args: Optional[Dict[str, Any]] = None,
|
|
221
|
+
) -> Dict[str, Any]:
|
|
222
|
+
r"""Calls a remote MCP server tool directly using the provided server
|
|
223
|
+
URL.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
server_url (str): The full URL for connecting to the MCP server
|
|
227
|
+
via Server-Sent Events (SSE).
|
|
228
|
+
tool_name (str): The name of the tool to call.
|
|
229
|
+
tool_args (Optional[Dict[str, Any]]): The input parameters for
|
|
230
|
+
the tool. Defaults to None, which might be treated as empty
|
|
231
|
+
args by the server. (default: :obj:`None`)
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Dict[str, Any]: Response containing the result of the tool call
|
|
235
|
+
or an error.
|
|
236
|
+
"""
|
|
237
|
+
endpoint = "/mcp-server/call-tool"
|
|
238
|
+
payload: Dict[str, Any] = {
|
|
239
|
+
"serverUrl": server_url,
|
|
240
|
+
"toolName": tool_name,
|
|
241
|
+
}
|
|
242
|
+
# Add toolArgs only if provided, otherwise server might expect empty
|
|
243
|
+
# dict
|
|
244
|
+
if tool_args is not None:
|
|
245
|
+
payload["toolArgs"] = tool_args
|
|
246
|
+
else:
|
|
247
|
+
# Explicitly setting to empty dict based on schema interpretation
|
|
248
|
+
payload["toolArgs"] = {}
|
|
249
|
+
|
|
250
|
+
headers = {'Content-Type': 'application/json'}
|
|
251
|
+
return self._request(
|
|
252
|
+
'POST', endpoint, payload=payload, additional_headers=headers
|
|
253
|
+
)
|
|
254
|
+
|
|
212
255
|
def get_tools(self) -> List[FunctionTool]:
|
|
213
256
|
r"""Returns a list of FunctionTool objects representing the functions
|
|
214
257
|
in the toolkit.
|
|
@@ -222,7 +265,8 @@ class KlavisToolkit(BaseToolkit):
|
|
|
222
265
|
FunctionTool(self.get_server_instance),
|
|
223
266
|
FunctionTool(self.delete_auth_data),
|
|
224
267
|
FunctionTool(self.delete_server_instance),
|
|
225
|
-
FunctionTool(self.get_server_tools),
|
|
226
268
|
FunctionTool(self.get_all_servers),
|
|
227
269
|
FunctionTool(self.set_auth_token),
|
|
270
|
+
FunctionTool(self.list_tools),
|
|
271
|
+
FunctionTool(self.call_tool),
|
|
228
272
|
]
|
camel/toolkits/mcp_toolkit.py
CHANGED
|
@@ -16,6 +16,7 @@ import json
|
|
|
16
16
|
import os
|
|
17
17
|
import shlex
|
|
18
18
|
from contextlib import AsyncExitStack, asynccontextmanager
|
|
19
|
+
from datetime import timedelta
|
|
19
20
|
from typing import (
|
|
20
21
|
TYPE_CHECKING,
|
|
21
22
|
Any,
|
|
@@ -52,14 +53,15 @@ class MCPClient(BaseToolkit):
|
|
|
52
53
|
|
|
53
54
|
Attributes:
|
|
54
55
|
command_or_url (str): URL for SSE mode or command executable for stdio
|
|
55
|
-
mode. (default: :obj:`
|
|
56
|
+
mode. (default: :obj:`None`)
|
|
56
57
|
args (List[str]): List of command-line arguments if stdio mode is used.
|
|
57
|
-
(default: :obj:`
|
|
58
|
+
(default: :obj:`None`)
|
|
58
59
|
env (Dict[str, str]): Environment variables for the stdio mode command.
|
|
59
|
-
(default: :obj:`
|
|
60
|
-
timeout (Optional[float]): Connection timeout.
|
|
60
|
+
(default: :obj:`None`)
|
|
61
|
+
timeout (Optional[float]): Connection timeout.
|
|
62
|
+
(default: :obj:`None`)
|
|
61
63
|
headers (Dict[str, str]): Headers for the HTTP request.
|
|
62
|
-
(default: :obj:`
|
|
64
|
+
(default: :obj:`None`)
|
|
63
65
|
strict (Optional[bool]): Whether to enforce strict mode for the
|
|
64
66
|
function call. (default: :obj:`False`)
|
|
65
67
|
"""
|
|
@@ -111,6 +113,7 @@ class MCPClient(BaseToolkit):
|
|
|
111
113
|
sse_client(
|
|
112
114
|
self.command_or_url,
|
|
113
115
|
headers=self.headers,
|
|
116
|
+
timeout=self.timeout,
|
|
114
117
|
)
|
|
115
118
|
)
|
|
116
119
|
else:
|
|
@@ -138,7 +141,11 @@ class MCPClient(BaseToolkit):
|
|
|
138
141
|
)
|
|
139
142
|
|
|
140
143
|
self._session = await self._exit_stack.enter_async_context(
|
|
141
|
-
ClientSession(
|
|
144
|
+
ClientSession(
|
|
145
|
+
read_stream,
|
|
146
|
+
write_stream,
|
|
147
|
+
timedelta(seconds=self.timeout) if self.timeout else None,
|
|
148
|
+
)
|
|
142
149
|
)
|
|
143
150
|
await self._session.initialize()
|
|
144
151
|
list_tools_result = await self.list_mcp_tools()
|
|
@@ -399,17 +406,20 @@ class MCPToolkit(BaseToolkit):
|
|
|
399
406
|
|
|
400
407
|
Args:
|
|
401
408
|
servers (Optional[List[MCPClient]]): List of MCPClient
|
|
402
|
-
instances to manage.
|
|
409
|
+
instances to manage. (default: :obj:`None`)
|
|
403
410
|
config_path (Optional[str]): Path to a JSON configuration file
|
|
404
|
-
defining MCP servers.
|
|
411
|
+
defining MCP servers. (default: :obj:`None`)
|
|
412
|
+
config_dict (Optional[Dict[str, Any]]): Dictionary containing MCP
|
|
413
|
+
server configurations in the same format as the config file.
|
|
414
|
+
(default: :obj:`None`)
|
|
405
415
|
strict (Optional[bool]): Whether to enforce strict mode for the
|
|
406
416
|
function call. (default: :obj:`False`)
|
|
407
417
|
|
|
408
418
|
Note:
|
|
409
|
-
Either `servers` or `
|
|
410
|
-
provided, servers from
|
|
419
|
+
Either `servers`, `config_path`, or `config_dict` must be provided.
|
|
420
|
+
If multiple are provided, servers from all sources will be combined.
|
|
411
421
|
|
|
412
|
-
For web servers in the config
|
|
422
|
+
For web servers in the config, you can specify authorization
|
|
413
423
|
headers using the "headers" field to connect to protected MCP server
|
|
414
424
|
endpoints.
|
|
415
425
|
|
|
@@ -438,14 +448,19 @@ class MCPToolkit(BaseToolkit):
|
|
|
438
448
|
self,
|
|
439
449
|
servers: Optional[List[MCPClient]] = None,
|
|
440
450
|
config_path: Optional[str] = None,
|
|
451
|
+
config_dict: Optional[Dict[str, Any]] = None,
|
|
441
452
|
strict: Optional[bool] = False,
|
|
442
453
|
):
|
|
443
454
|
super().__init__()
|
|
444
455
|
|
|
445
|
-
|
|
456
|
+
sources_provided = sum(
|
|
457
|
+
1 for src in [servers, config_path, config_dict] if src is not None
|
|
458
|
+
)
|
|
459
|
+
if sources_provided > 1:
|
|
446
460
|
logger.warning(
|
|
447
|
-
"
|
|
448
|
-
"Servers from
|
|
461
|
+
"Multiple configuration sources provided "
|
|
462
|
+
f"({sources_provided}). Servers from all sources "
|
|
463
|
+
"will be combined."
|
|
449
464
|
)
|
|
450
465
|
|
|
451
466
|
self.servers: List[MCPClient] = servers or []
|
|
@@ -455,6 +470,9 @@ class MCPToolkit(BaseToolkit):
|
|
|
455
470
|
self._load_servers_from_config(config_path, strict)
|
|
456
471
|
)
|
|
457
472
|
|
|
473
|
+
if config_dict:
|
|
474
|
+
self.servers.extend(self._load_servers_from_dict(config_dict))
|
|
475
|
+
|
|
458
476
|
self._exit_stack = AsyncExitStack()
|
|
459
477
|
self._connected = False
|
|
460
478
|
|
|
@@ -484,9 +502,25 @@ class MCPToolkit(BaseToolkit):
|
|
|
484
502
|
logger.warning(f"Config file not found: '{config_path}'")
|
|
485
503
|
raise e
|
|
486
504
|
|
|
505
|
+
return self._load_servers_from_dict(config=data, strict=strict)
|
|
506
|
+
|
|
507
|
+
def _load_servers_from_dict(
|
|
508
|
+
self, config: Dict[str, Any], strict: Optional[bool] = False
|
|
509
|
+
) -> List[MCPClient]:
|
|
510
|
+
r"""Loads MCP server configurations from a dictionary.
|
|
511
|
+
|
|
512
|
+
Args:
|
|
513
|
+
config (Dict[str, Any]): Dictionary containing server
|
|
514
|
+
configurations.
|
|
515
|
+
strict (bool): Whether to enforce strict mode for the
|
|
516
|
+
function call. (default: :obj:`False`)
|
|
517
|
+
|
|
518
|
+
Returns:
|
|
519
|
+
List[MCPClient]: List of configured MCPClient instances.
|
|
520
|
+
"""
|
|
487
521
|
all_servers = []
|
|
488
522
|
|
|
489
|
-
mcp_servers =
|
|
523
|
+
mcp_servers = config.get("mcpServers", {})
|
|
490
524
|
if not isinstance(mcp_servers, dict):
|
|
491
525
|
logger.warning("'mcpServers' is not a dictionary, skipping...")
|
|
492
526
|
mcp_servers = {}
|
|
@@ -505,11 +539,16 @@ class MCPToolkit(BaseToolkit):
|
|
|
505
539
|
)
|
|
506
540
|
continue
|
|
507
541
|
|
|
542
|
+
# Include headers if provided in the configuration
|
|
543
|
+
headers = cfg.get("headers", {})
|
|
544
|
+
|
|
545
|
+
cmd_or_url = cast(str, cfg.get("command") or cfg.get("url"))
|
|
508
546
|
server = MCPClient(
|
|
509
|
-
command_or_url=
|
|
547
|
+
command_or_url=cmd_or_url,
|
|
510
548
|
args=cfg.get("args", []),
|
|
511
549
|
env={**os.environ, **cfg.get("env", {})},
|
|
512
550
|
timeout=cfg.get("timeout", None),
|
|
551
|
+
headers=headers,
|
|
513
552
|
strict=strict,
|
|
514
553
|
)
|
|
515
554
|
all_servers.append(server)
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
from typing import List, Optional
|
|
16
|
+
|
|
17
|
+
from camel.toolkits import BaseToolkit, FunctionTool
|
|
18
|
+
|
|
19
|
+
from .mcp_toolkit import MCPToolkit
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class PlaywrightMCPToolkit(BaseToolkit):
|
|
23
|
+
r"""PlaywrightMCPToolkit provides an interface for interacting with web
|
|
24
|
+
browsers using the Playwright automation library through the Model Context
|
|
25
|
+
Protocol (MCP).
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
timeout (Optional[float]): Connection timeout in seconds.
|
|
29
|
+
(default: :obj:`None`)
|
|
30
|
+
|
|
31
|
+
Note:
|
|
32
|
+
Currently only supports asynchronous operation mode.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, timeout: Optional[float] = None) -> None:
|
|
36
|
+
r"""Initializes the PlaywrightMCPToolkit with the specified timeout.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
timeout (Optional[float]): Connection timeout in seconds.
|
|
40
|
+
(default: :obj:`None`)
|
|
41
|
+
"""
|
|
42
|
+
super().__init__(timeout=timeout)
|
|
43
|
+
|
|
44
|
+
self._mcp_toolkit = MCPToolkit(
|
|
45
|
+
config_dict={
|
|
46
|
+
"mcpServers": {
|
|
47
|
+
"playwright": {
|
|
48
|
+
"command": "npx",
|
|
49
|
+
"args": ["@playwright/mcp@latest"],
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
async def connect(self):
|
|
56
|
+
r"""Explicitly connect to the Playwright MCP server."""
|
|
57
|
+
await self._mcp_toolkit.connect()
|
|
58
|
+
|
|
59
|
+
async def disconnect(self):
|
|
60
|
+
r"""Explicitly disconnect from the Playwright MCP server."""
|
|
61
|
+
await self._mcp_toolkit.disconnect()
|
|
62
|
+
|
|
63
|
+
def get_tools(self) -> List[FunctionTool]:
|
|
64
|
+
r"""Returns a list of tools provided by the PlaywrightMCPToolkit.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
List[FunctionTool]: List of available tools.
|
|
68
|
+
"""
|
|
69
|
+
return self._mcp_toolkit.get_tools()
|
|
@@ -0,0 +1,132 @@
|
|
|
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
|
+
from typing import Any, Dict, List, Optional
|
|
15
|
+
|
|
16
|
+
import requests
|
|
17
|
+
|
|
18
|
+
from camel.toolkits.base import BaseToolkit
|
|
19
|
+
from camel.toolkits.function_tool import FunctionTool
|
|
20
|
+
from camel.utils import MCPServer
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@MCPServer()
|
|
24
|
+
class PulseMCPSearchToolkit(BaseToolkit):
|
|
25
|
+
r"""A toolkit for searching MCP servers using the PulseMCP API."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, timeout: Optional[float] = None):
|
|
28
|
+
super().__init__(timeout=timeout)
|
|
29
|
+
self.api_base_url = "https://api.pulsemcp.com/v0beta"
|
|
30
|
+
|
|
31
|
+
def search_mcp_servers(
|
|
32
|
+
self,
|
|
33
|
+
query: Optional[str] = None,
|
|
34
|
+
top_k: Optional[int] = 5,
|
|
35
|
+
package_registry: Optional[str] = None,
|
|
36
|
+
count_per_page: int = 5000,
|
|
37
|
+
offset: int = 0,
|
|
38
|
+
) -> Dict[str, Any]:
|
|
39
|
+
r"""Search for MCP servers using the PulseMCP API.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
query (Optional[str]): The query to search for.
|
|
43
|
+
(default: :obj:`None`)
|
|
44
|
+
top_k (Optional[int]): After sorting, return only the top_k
|
|
45
|
+
servers. (default: :obj:`5`)
|
|
46
|
+
package_registry (Optional[str]): The package registry to search
|
|
47
|
+
for. (default: :obj:`None`)
|
|
48
|
+
count_per_page (int): The number of servers to return per page.
|
|
49
|
+
(default: :obj:`5000`)
|
|
50
|
+
offset (int): The offset to start the search from.
|
|
51
|
+
(default: :obj:`0`)
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Dict[str, Any]: A dictionary containing the search results or
|
|
55
|
+
an error message.
|
|
56
|
+
"""
|
|
57
|
+
params: Dict[str, Any] = {
|
|
58
|
+
"count_per_page": min(count_per_page, 5000),
|
|
59
|
+
"offset": offset,
|
|
60
|
+
}
|
|
61
|
+
if query:
|
|
62
|
+
params["query"] = query
|
|
63
|
+
|
|
64
|
+
response = requests.get(f"{self.api_base_url}/servers", params=params)
|
|
65
|
+
|
|
66
|
+
if response.status_code != 200:
|
|
67
|
+
error_msg = f"Error searching MCP servers: {response.status_code}"
|
|
68
|
+
if response.text:
|
|
69
|
+
try:
|
|
70
|
+
error_info = response.json()
|
|
71
|
+
if "error" in error_info:
|
|
72
|
+
msg = error_info['error'].get('message', '')
|
|
73
|
+
error_msg = f"{error_msg} - {msg}"
|
|
74
|
+
except Exception:
|
|
75
|
+
error_msg = f"{error_msg} - {response.text}"
|
|
76
|
+
return {"error": error_msg}
|
|
77
|
+
|
|
78
|
+
data = response.json()
|
|
79
|
+
servers = data.get("servers", [])
|
|
80
|
+
|
|
81
|
+
if package_registry:
|
|
82
|
+
package_registry_lower = package_registry.lower()
|
|
83
|
+
servers = [
|
|
84
|
+
server
|
|
85
|
+
for server in servers
|
|
86
|
+
if isinstance(server.get("package_registry"), str)
|
|
87
|
+
and server.get("package_registry").lower()
|
|
88
|
+
== package_registry_lower
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
if query:
|
|
92
|
+
query_lower = query.lower()
|
|
93
|
+
# Helper function to calculate score
|
|
94
|
+
# 1. With name +5
|
|
95
|
+
# 2. With description +3
|
|
96
|
+
# 3. With stars +1
|
|
97
|
+
|
|
98
|
+
def score(server: Dict[str, Any]) -> float:
|
|
99
|
+
name = server.get("name", "").lower()
|
|
100
|
+
desc = server.get("short_description", "").lower()
|
|
101
|
+
stars = server.get("github_stars", 0) or 0
|
|
102
|
+
score: float = 0.0
|
|
103
|
+
if query_lower in name:
|
|
104
|
+
score += 5
|
|
105
|
+
if query_lower in desc:
|
|
106
|
+
score += 3
|
|
107
|
+
score += stars / 1000
|
|
108
|
+
return score
|
|
109
|
+
|
|
110
|
+
servers = sorted(servers, key=score, reverse=True)
|
|
111
|
+
else:
|
|
112
|
+
servers = sorted(
|
|
113
|
+
servers,
|
|
114
|
+
key=lambda x: x.get("github_stars", 0) or 0,
|
|
115
|
+
reverse=True,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if top_k is not None:
|
|
119
|
+
servers = servers[:top_k]
|
|
120
|
+
|
|
121
|
+
return {"servers": servers}
|
|
122
|
+
|
|
123
|
+
def get_tools(self) -> List[FunctionTool]:
|
|
124
|
+
r"""Returns a list of FunctionTool objects representing the
|
|
125
|
+
functions in the toolkit.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
List[FunctionTool]: A list of FunctionTool objects.
|
|
129
|
+
"""
|
|
130
|
+
return [
|
|
131
|
+
FunctionTool(self.search_mcp_servers),
|
|
132
|
+
]
|
camel/types/enums.py
CHANGED
|
@@ -196,6 +196,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
196
196
|
MISTRAL_MIXTRAL_8x22B = "open-mixtral-8x22b"
|
|
197
197
|
MISTRAL_NEMO = "open-mistral-nemo"
|
|
198
198
|
MISTRAL_PIXTRAL_12B = "pixtral-12b-2409"
|
|
199
|
+
MISTRAL_MEDIUM_3 = "mistral-medium-latest"
|
|
199
200
|
|
|
200
201
|
# Reka models
|
|
201
202
|
REKA_CORE = "reka-core"
|
|
@@ -475,6 +476,16 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
475
476
|
ModelType.GPT_4_TURBO,
|
|
476
477
|
ModelType.GPT_4O,
|
|
477
478
|
ModelType.GPT_4O_MINI,
|
|
479
|
+
ModelType.O1,
|
|
480
|
+
ModelType.O1_PREVIEW,
|
|
481
|
+
ModelType.O1_MINI,
|
|
482
|
+
ModelType.O3_MINI,
|
|
483
|
+
ModelType.GPT_4_5_PREVIEW,
|
|
484
|
+
ModelType.GPT_4_1,
|
|
485
|
+
ModelType.GPT_4_1_MINI,
|
|
486
|
+
ModelType.GPT_4_1_NANO,
|
|
487
|
+
ModelType.O4_MINI,
|
|
488
|
+
ModelType.O3,
|
|
478
489
|
}
|
|
479
490
|
|
|
480
491
|
@property
|
|
@@ -586,6 +597,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
586
597
|
ModelType.MISTRAL_PIXTRAL_12B,
|
|
587
598
|
ModelType.MISTRAL_8B,
|
|
588
599
|
ModelType.MISTRAL_3B,
|
|
600
|
+
ModelType.MISTRAL_MEDIUM_3,
|
|
589
601
|
}
|
|
590
602
|
|
|
591
603
|
@property
|
|
@@ -1094,6 +1106,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
1094
1106
|
ModelType.NETMIND_DEEPSEEK_R1,
|
|
1095
1107
|
ModelType.NETMIND_DEEPSEEK_V3,
|
|
1096
1108
|
ModelType.NOVITA_DEEPSEEK_V3_0324,
|
|
1109
|
+
ModelType.MISTRAL_MEDIUM_3,
|
|
1097
1110
|
}:
|
|
1098
1111
|
return 128_000
|
|
1099
1112
|
elif self in {
|
|
@@ -1389,6 +1402,7 @@ class ModelPlatformType(Enum):
|
|
|
1389
1402
|
MISTRAL = "mistral"
|
|
1390
1403
|
REKA = "reka"
|
|
1391
1404
|
TOGETHER = "together"
|
|
1405
|
+
STUB = "stub"
|
|
1392
1406
|
OPENAI_COMPATIBLE_MODEL = "openai-compatible-model"
|
|
1393
1407
|
SAMBA = "samba-nova"
|
|
1394
1408
|
COHERE = "cohere"
|
|
@@ -1577,6 +1591,11 @@ class ModelPlatformType(Enum):
|
|
|
1577
1591
|
r"""Returns whether this platform is Novita."""
|
|
1578
1592
|
return self is ModelPlatformType.NOVITA
|
|
1579
1593
|
|
|
1594
|
+
@property
|
|
1595
|
+
def is_watsonx(self) -> bool:
|
|
1596
|
+
r"""Returns whether this platform is WatsonX."""
|
|
1597
|
+
return self is ModelPlatformType.WATSONX
|
|
1598
|
+
|
|
1580
1599
|
|
|
1581
1600
|
class AudioModelType(Enum):
|
|
1582
1601
|
TTS_1 = "tts-1"
|
camel/utils/__init__.py
CHANGED
|
@@ -44,7 +44,7 @@ from .constants import Constants
|
|
|
44
44
|
from .deduplication import DeduplicationResult, deduplicate_internally
|
|
45
45
|
from .filename import sanitize_filename
|
|
46
46
|
from .mcp import MCPServer
|
|
47
|
-
from .response_format import get_pydantic_model
|
|
47
|
+
from .response_format import get_pydantic_model, model_from_json_schema
|
|
48
48
|
from .token_counting import (
|
|
49
49
|
AnthropicTokenCounter,
|
|
50
50
|
BaseTokenCounter,
|
|
@@ -92,6 +92,7 @@ __all__ = [
|
|
|
92
92
|
"BatchProcessor",
|
|
93
93
|
"with_timeout",
|
|
94
94
|
"MCPServer",
|
|
95
|
+
"model_from_json_schema",
|
|
95
96
|
"sanitize_filename",
|
|
96
97
|
"browser_toolkit_save_auth_cookie",
|
|
97
98
|
]
|
camel/utils/response_format.py
CHANGED
|
@@ -16,9 +16,9 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
import inspect
|
|
18
18
|
import json
|
|
19
|
-
from typing import Callable, Type, Union
|
|
19
|
+
from typing import Any, Callable, Dict, List, Optional, Type, Union
|
|
20
20
|
|
|
21
|
-
from pydantic import BaseModel, create_model
|
|
21
|
+
from pydantic import BaseModel, Field, create_model
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def get_pydantic_model(
|
|
@@ -61,3 +61,85 @@ def get_pydantic_model(
|
|
|
61
61
|
if issubclass(input_data, BaseModel):
|
|
62
62
|
return input_data
|
|
63
63
|
raise ValueError("Invalid input data provided.")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
TYPE_MAPPING = {
|
|
67
|
+
"integer": int,
|
|
68
|
+
"number": float,
|
|
69
|
+
"string": str,
|
|
70
|
+
"boolean": bool,
|
|
71
|
+
"array": list,
|
|
72
|
+
"object": dict,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def model_from_json_schema(
|
|
77
|
+
name: str,
|
|
78
|
+
schema: Dict[str, Any],
|
|
79
|
+
) -> Type[BaseModel]:
|
|
80
|
+
r"""Create a Pydantic model from a JSON schema.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
name (str): The name of the model.
|
|
84
|
+
schema (Dict[str, Any]): The JSON schema to create the model from.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Type[BaseModel]: The Pydantic model.
|
|
88
|
+
"""
|
|
89
|
+
properties = schema.get("properties", {})
|
|
90
|
+
required_fields = set(schema.get("required", []))
|
|
91
|
+
fields: Dict[str, Any] = {}
|
|
92
|
+
|
|
93
|
+
for field_name, field_schema in properties.items():
|
|
94
|
+
json_type = field_schema.get("type", "string")
|
|
95
|
+
# Handle nested objects recursively.
|
|
96
|
+
if json_type == "object" and "properties" in field_schema:
|
|
97
|
+
py_type = model_from_json_schema(
|
|
98
|
+
f"{name}_{field_name}", field_schema
|
|
99
|
+
)
|
|
100
|
+
elif json_type == "array":
|
|
101
|
+
# Process array items if available.
|
|
102
|
+
items_schema = field_schema.get("items", {"type": "string"})
|
|
103
|
+
items_type: Type[Any] = TYPE_MAPPING.get(
|
|
104
|
+
items_schema.get("type", "string"), str
|
|
105
|
+
)
|
|
106
|
+
if (
|
|
107
|
+
items_schema.get("type") == "object"
|
|
108
|
+
and "properties" in items_schema
|
|
109
|
+
):
|
|
110
|
+
items_type = model_from_json_schema(
|
|
111
|
+
f"{name}_{field_name}_item", items_schema
|
|
112
|
+
)
|
|
113
|
+
py_type = List[items_type] # type: ignore[assignment, valid-type]
|
|
114
|
+
else:
|
|
115
|
+
py_type = TYPE_MAPPING.get(json_type, str)
|
|
116
|
+
|
|
117
|
+
# Handle nullable fields.
|
|
118
|
+
if field_schema.get("nullable", False):
|
|
119
|
+
py_type = Optional[py_type] # type: ignore[assignment]
|
|
120
|
+
|
|
121
|
+
# Construct constraints if available.
|
|
122
|
+
constraints = {}
|
|
123
|
+
if "minLength" in field_schema:
|
|
124
|
+
constraints["min_length"] = field_schema["minLength"]
|
|
125
|
+
if "maxLength" in field_schema:
|
|
126
|
+
constraints["max_length"] = field_schema["maxLength"]
|
|
127
|
+
if "minimum" in field_schema:
|
|
128
|
+
constraints["ge"] = field_schema["minimum"]
|
|
129
|
+
if "maximum" in field_schema:
|
|
130
|
+
constraints["le"] = field_schema["maximum"]
|
|
131
|
+
if "enum" in field_schema:
|
|
132
|
+
constraints["enum"] = field_schema["enum"]
|
|
133
|
+
if "description" in field_schema:
|
|
134
|
+
constraints["description"] = field_schema["description"]
|
|
135
|
+
|
|
136
|
+
default_value = field_schema.get("default", None)
|
|
137
|
+
if field_name in required_fields:
|
|
138
|
+
fields[field_name] = (py_type, Field(..., **constraints))
|
|
139
|
+
else:
|
|
140
|
+
fields[field_name] = (
|
|
141
|
+
Optional[py_type],
|
|
142
|
+
Field(default_value, **constraints),
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return create_model(name, **fields) # type: ignore[call-overload]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
camel/__init__.py,sha256=
|
|
1
|
+
camel/__init__.py,sha256=YtkzoktMmiyIItoI8OWaV4sxG0yQ8KBjS65mn83J5Jo,912
|
|
2
2
|
camel/generators.py,sha256=JRqj9_m1PF4qT6UtybzTQ-KBT9MJQt18OAAYvQ_fr2o,13844
|
|
3
3
|
camel/human.py,sha256=9X09UmxI2JqQnhrFfnZ3B9EzFmVfdSWQcjLWTIXKXe0,4962
|
|
4
4
|
camel/logger.py,sha256=rZVeOVYuQ9RYJ5Tqyv0usqy0g4zaVEq4qSfZ9nd2640,5755
|
|
@@ -7,7 +7,7 @@ camel/agents/__init__.py,sha256=64weKqdvmpZcGWyVkO-OKASAmVUdrQjv60JApgPk_SA,1644
|
|
|
7
7
|
camel/agents/_types.py,sha256=ryPRmEXnpNtbFT23GoAcwK-zxWWsIOqYu64mxMx_PhI,1430
|
|
8
8
|
camel/agents/_utils.py,sha256=AR7Qqgbkmn4X2edYUQf1rdksGUyV5hm3iK1z-Dn0Mcg,6266
|
|
9
9
|
camel/agents/base.py,sha256=c4bJYL3G3Z41SaFdMPMn8ZjLdFiFaVOFO6EQIfuCVR8,1124
|
|
10
|
-
camel/agents/chat_agent.py,sha256=
|
|
10
|
+
camel/agents/chat_agent.py,sha256=s19qJ0eK_bVX6P-yIoazO4cMUeAumwVY1XFNVKaDfVo,59281
|
|
11
11
|
camel/agents/critic_agent.py,sha256=qFVlHlQo0CVgmPWfWYLT8_oP_KyzCLFsQw_nN_vu5Bs,7487
|
|
12
12
|
camel/agents/deductive_reasoner_agent.py,sha256=6BZGaq1hR6hKJuQtOfoYQnk_AkZpw_Mr7mUy2MspQgs,13540
|
|
13
13
|
camel/agents/embodied_agent.py,sha256=XBxBu5ZMmSJ4B2U3Z7SMwvLlgp6yNpaBe8HNQmY9CZA,7536
|
|
@@ -176,8 +176,8 @@ camel/models/groq_model.py,sha256=596VqRJ_yxv9Jz3sG7UVXVkIjZI1nX7zQAD709m4uig,37
|
|
|
176
176
|
camel/models/internlm_model.py,sha256=l7WjJ7JISCCqkezhEXzmjj_Mvhqhxxhsg4NuenP7w9w,4374
|
|
177
177
|
camel/models/litellm_model.py,sha256=rlSt3EnBAAYyoIxq0_XTuRmRnc4RWvD2Z14yIrI_7uw,5942
|
|
178
178
|
camel/models/lmstudio_model.py,sha256=_Lnv0e2ichks_MrNJGNIawEtGtP7T_xX8v0bFNNeWes,3641
|
|
179
|
-
camel/models/mistral_model.py,sha256=
|
|
180
|
-
camel/models/model_factory.py,sha256=
|
|
179
|
+
camel/models/mistral_model.py,sha256=3tT59xJO0rwZK0Gs0RXtV6TC9g6uEO9gD7D_-NzhHDc,13399
|
|
180
|
+
camel/models/model_factory.py,sha256=e-dRSZgNGXCgjgdxDl125qtdts7o9M0RQ6mY8qzoBO0,11470
|
|
181
181
|
camel/models/model_manager.py,sha256=gfpL-WUxuTXgNeCkIVg8Y0zRvxMqRLX8JGt0XEAPQ8Y,9214
|
|
182
182
|
camel/models/modelscope_model.py,sha256=aI7i50DSIE6MO2U_WvULan6Sk4b5d7iZoEHQaARo4FA,10487
|
|
183
183
|
camel/models/moonshot_model.py,sha256=yeD2jrfQpFaWJHVe1s1KrI6aHQVzKRcBDt9C2Qo4nU8,4305
|
|
@@ -295,7 +295,7 @@ camel/terminators/__init__.py,sha256=t8uqrkUnXEOYMXQDgaBkMFJ0EXFKI0kmx4cUimli3Ls
|
|
|
295
295
|
camel/terminators/base.py,sha256=xmJzERX7GdSXcxZjAHHODa0rOxRChMSRboDCNHWSscs,1511
|
|
296
296
|
camel/terminators/response_terminator.py,sha256=n3G5KP6Oj7-7WlRN0yFcrtLpqAJKaKS0bmhrWlFfCgQ,4982
|
|
297
297
|
camel/terminators/token_limit_terminator.py,sha256=YWv6ZR8R9yI2Qnf_3xES5bEE_O5bb2CxQ0EUXfMh34c,2118
|
|
298
|
-
camel/toolkits/__init__.py,sha256=
|
|
298
|
+
camel/toolkits/__init__.py,sha256=JbvxFf-wUFClNSriXB56LkhaRipw4n4PEc2M9f5-47o,4657
|
|
299
299
|
camel/toolkits/aci_toolkit.py,sha256=jhXMQggG22hd3dXdT3iJm7qWTH3KJC-TUVk1txoNWrM,16079
|
|
300
300
|
camel/toolkits/arxiv_toolkit.py,sha256=Bs2-K1yfmqhEhHoQ0j00KoI8LpOd8M3ApXcvI_-ApVw,6303
|
|
301
301
|
camel/toolkits/ask_news_toolkit.py,sha256=WfWaqwEo1Apbil3-Rb5y65Ws43NU4rAFWZu5VHe4los,23448
|
|
@@ -316,10 +316,10 @@ camel/toolkits/google_scholar_toolkit.py,sha256=WQ9a0HQr0vro1Uo1m--alCUXvMmaHVKe
|
|
|
316
316
|
camel/toolkits/human_toolkit.py,sha256=ZbhXPA0G3mQkcNW_jPwywReAft2pIJG0WkVXOG48s1w,2328
|
|
317
317
|
camel/toolkits/image_analysis_toolkit.py,sha256=gnbX2by7_pxPpjUEY8bOj1tV-hGC3mwqEvQRMXyZ9TM,7535
|
|
318
318
|
camel/toolkits/jina_reranker_toolkit.py,sha256=U-V9qZ7SKP3SPTh_0CsE7ZKNqS9jNkmKYsIN1Mk-vFY,8118
|
|
319
|
-
camel/toolkits/klavis_toolkit.py,sha256=
|
|
319
|
+
camel/toolkits/klavis_toolkit.py,sha256=ZKerhgz5e-AV-iv0ftf07HgWikknIHjB3EOQswfuR80,9864
|
|
320
320
|
camel/toolkits/linkedin_toolkit.py,sha256=wn4eXwYYlVA7doTna7k7WYhUqTBF83W79S-UJs_IQr0,8065
|
|
321
321
|
camel/toolkits/math_toolkit.py,sha256=KdI8AJ9Dbq5cfWboAYJUYgSkmADMCO5eZ6yqYkWuiIQ,3686
|
|
322
|
-
camel/toolkits/mcp_toolkit.py,sha256=
|
|
322
|
+
camel/toolkits/mcp_toolkit.py,sha256=a2Jdf0TyxqB-P1hB_dh33nH88cEunFMnh7qjgs1H1aQ,21996
|
|
323
323
|
camel/toolkits/memory_toolkit.py,sha256=TeKYd5UMwgjVpuS2orb-ocFL13eUNKujvrFOruDCpm8,4436
|
|
324
324
|
camel/toolkits/meshy_toolkit.py,sha256=NbgdOBD3FYLtZf-AfonIv6-Q8-8DW129jsaP1PqI2rs,7126
|
|
325
325
|
camel/toolkits/mineru_toolkit.py,sha256=vRX9LholLNkpbJ6axfEN4pTG85aWb0PDmlVy3rAAXhg,6868
|
|
@@ -329,7 +329,9 @@ camel/toolkits/open_api_toolkit.py,sha256=Venfq8JwTMQfzRzzB7AYmYUMEX35hW0BjIv_oz
|
|
|
329
329
|
camel/toolkits/openai_agent_toolkit.py,sha256=hT2ancdQigngAiY1LNnGJzZeiBDHUxrRGv6BdZTJizc,4696
|
|
330
330
|
camel/toolkits/openbb_toolkit.py,sha256=8yBZL9E2iSgskosBQhD3pTP56oV6gerWpFjIJc_2UMo,28935
|
|
331
331
|
camel/toolkits/page_script.js,sha256=gypbuQ_gn_oa3rQDoCN_q-kJ0jND1eSvY-30PufPZmQ,12613
|
|
332
|
+
camel/toolkits/playwright_mcp_toolkit.py,sha256=_TcCRA3ECaWO0pqUjoZ2whfWXgvank4A4isqNjgZqqc,2403
|
|
332
333
|
camel/toolkits/pubmed_toolkit.py,sha256=VGl8KeyWi7pjb2kEhFBLmpBlP9ezv8JyWRHtEVTQ6nQ,12227
|
|
334
|
+
camel/toolkits/pulse_mcp_search_toolkit.py,sha256=uLUpm19uC_4xLJow0gGVS9f-5T5EW2iRAXdJ4nqJG-A,4783
|
|
333
335
|
camel/toolkits/pyautogui_toolkit.py,sha256=Q810fm8cFvElRory7B74aqS2YV6BOpdRE6jkewoM8xc,16093
|
|
334
336
|
camel/toolkits/reddit_toolkit.py,sha256=x0XAT1zQJVNHUr1R1HwWCgIlkamU-kPmbfb_H1WIv-w,8036
|
|
335
337
|
camel/toolkits/retrieval_toolkit.py,sha256=BKjEyOqW3cGEPTS5yHPYb-Qg795iNNPIs1wjowfuq3U,3825
|
|
@@ -373,19 +375,19 @@ camel/toolkits/open_api_specs/web_scraper/openapi.yaml,sha256=u_WalQ01e8W1D27VnZ
|
|
|
373
375
|
camel/toolkits/open_api_specs/web_scraper/paths/__init__.py,sha256=OKCZrQCDwaWtXIN_2rA9FSqEvgpQRieRoHh7Ek6N16A,702
|
|
374
376
|
camel/toolkits/open_api_specs/web_scraper/paths/scraper.py,sha256=aWy1_ppV4NVVEZfnbN3tu9XA9yAPAC9bRStJ5JuXMRU,1117
|
|
375
377
|
camel/types/__init__.py,sha256=Xdkjh7TQDAQUQ7pFnt7i_rLjjOhsJJnhzusARcFc94Q,2311
|
|
376
|
-
camel/types/enums.py,sha256=
|
|
378
|
+
camel/types/enums.py,sha256=ULkVZwiCBO6tjemF8V_A7BlYsSWJH1owp2UVDE39N4A,58317
|
|
377
379
|
camel/types/openai_types.py,sha256=8ZFzLe-zGmKNPfuVZFzxlxAX98lGf18gtrPhOgMmzus,2104
|
|
378
380
|
camel/types/unified_model_type.py,sha256=TpiUmJ3IuX8LNLtTUeUcVM7U82r4ClSq3ZQlNX3ODKs,5351
|
|
379
381
|
camel/types/agents/__init__.py,sha256=cbvVkogPoZgcwZrgxLH6EtpGXk0kavF79nOic0Dc1vg,786
|
|
380
382
|
camel/types/agents/tool_calling_record.py,sha256=qa-vLyKvYzWprRkFFl1928xaw9CnfacIebHaqM-oY3s,1814
|
|
381
|
-
camel/utils/__init__.py,sha256=
|
|
383
|
+
camel/utils/__init__.py,sha256=v4ugilUllTQGUSjnCfDvpoFGhWo3JMA7JRZlBceftic,2869
|
|
382
384
|
camel/utils/async_func.py,sha256=KqoktGSWjZBuAMQ2CV0X6FRgHGlzCKLfeaWvp-f1Qz8,1568
|
|
383
385
|
camel/utils/commons.py,sha256=3XofL3nZ-tZ3DQLWmP41CAp8lT-ictBBsttJdRJjL4Q,36323
|
|
384
386
|
camel/utils/constants.py,sha256=cqnxmpUeOwrtsR-tRO4bbOc6ZP19TLj7avjm3FONMJs,1410
|
|
385
387
|
camel/utils/deduplication.py,sha256=UHikAtOW1TTDunf2t_wa2kFbmkrXWf7HfOKwLvwCxzo,8958
|
|
386
388
|
camel/utils/filename.py,sha256=HYNc1wbSCgNR1CN21cwHxdAhpnsf5ySJ6jUDfeqOK20,2532
|
|
387
389
|
camel/utils/mcp.py,sha256=iuthL8VuUXIRU34Nvx8guq7frfglpZoxewUKuAg3e1s,5077
|
|
388
|
-
camel/utils/response_format.py,sha256=
|
|
390
|
+
camel/utils/response_format.py,sha256=xZcx6xBxeg3A0e7R0JCMJdNm2oQ1-diqVLs0JsiCkZU,5319
|
|
389
391
|
camel/utils/token_counting.py,sha256=apkERzNoVc4sgvJvWVosvepX3KH8pVypVjrL4AA7RB4,17521
|
|
390
392
|
camel/utils/chunker/__init__.py,sha256=6iN6HL6sblIjDuJTILk-9qKcHBZ97t8b6tZCWPZ0OYI,899
|
|
391
393
|
camel/utils/chunker/base.py,sha256=9CuqymFCRncyAdEST-IcRonB732YAPhusznqH-RES3E,960
|
|
@@ -397,7 +399,7 @@ camel/verifiers/math_verifier.py,sha256=tA1D4S0sm8nsWISevxSN0hvSVtIUpqmJhzqfbuMo
|
|
|
397
399
|
camel/verifiers/models.py,sha256=GdxYPr7UxNrR1577yW4kyroRcLGfd-H1GXgv8potDWU,2471
|
|
398
400
|
camel/verifiers/physics_verifier.py,sha256=c1grrRddcrVN7szkxhv2QirwY9viIRSITWeWFF5HmLs,30187
|
|
399
401
|
camel/verifiers/python_verifier.py,sha256=ogTz77wODfEcDN4tMVtiSkRQyoiZbHPY2fKybn59lHw,20558
|
|
400
|
-
camel_ai-0.2.
|
|
401
|
-
camel_ai-0.2.
|
|
402
|
-
camel_ai-0.2.
|
|
403
|
-
camel_ai-0.2.
|
|
402
|
+
camel_ai-0.2.56.dist-info/METADATA,sha256=EJjtjqfftYHbjaVC_Nv8Kavlpf32Kj685q-2sE98GH4,44254
|
|
403
|
+
camel_ai-0.2.56.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
404
|
+
camel_ai-0.2.56.dist-info/licenses/LICENSE,sha256=id0nB2my5kG0xXeimIu5zZrbHLS6EQvxvkKkzIHaT2k,11343
|
|
405
|
+
camel_ai-0.2.56.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|