camel-ai 0.2.38__py3-none-any.whl → 0.2.39__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 +4 -0
- camel/agents/repo_agent.py +2 -2
- camel/benchmarks/apibank.py +1 -1
- camel/benchmarks/apibench.py +1 -1
- camel/configs/__init__.py +3 -0
- camel/configs/modelscope_config.py +59 -0
- camel/datagen/self_improving_cot.py +1 -1
- camel/datasets/__init__.py +2 -0
- camel/datasets/base_generator.py +22 -9
- camel/datasets/few_shot_generator.py +2 -3
- camel/datasets/self_instruct_generator.py +415 -0
- camel/embeddings/openai_compatible_embedding.py +13 -5
- camel/environments/models.py +1 -1
- camel/environments/single_step.py +155 -89
- camel/interpreters/docker_interpreter.py +1 -1
- camel/interpreters/internal_python_interpreter.py +1 -1
- camel/loaders/unstructured_io.py +2 -1
- camel/memories/blocks/chat_history_block.py +1 -1
- camel/memories/context_creators/score_based.py +2 -2
- camel/models/__init__.py +2 -0
- camel/models/model_factory.py +119 -0
- camel/models/modelscope_model.py +208 -0
- camel/models/openai_audio_models.py +2 -2
- camel/models/openai_model.py +49 -2
- camel/models/togetherai_model.py +2 -2
- camel/models/vllm_model.py +1 -1
- camel/models/zhipuai_model.py +2 -2
- camel/retrievers/vector_retriever.py +1 -1
- camel/storages/graph_storages/neo4j_graph.py +1 -1
- camel/storages/vectordb_storages/base.py +2 -2
- camel/storages/vectordb_storages/milvus.py +2 -2
- camel/storages/vectordb_storages/qdrant.py +2 -2
- camel/tasks/task.py +2 -2
- camel/toolkits/__init__.py +4 -1
- camel/toolkits/arxiv_toolkit.py +2 -1
- camel/toolkits/ask_news_toolkit.py +11 -3
- camel/toolkits/audio_analysis_toolkit.py +2 -0
- camel/toolkits/base.py +3 -0
- camel/toolkits/code_execution.py +3 -1
- camel/toolkits/dappier_toolkit.py +2 -1
- camel/toolkits/data_commons_toolkit.py +2 -0
- camel/toolkits/excel_toolkit.py +2 -0
- camel/toolkits/file_write_toolkit.py +2 -0
- camel/toolkits/github_toolkit.py +6 -4
- camel/toolkits/google_scholar_toolkit.py +2 -0
- camel/toolkits/human_toolkit.py +17 -1
- camel/toolkits/image_analysis_toolkit.py +2 -0
- camel/toolkits/linkedin_toolkit.py +2 -1
- camel/toolkits/math_toolkit.py +2 -0
- camel/toolkits/mcp_toolkit.py +42 -52
- camel/toolkits/meshy_toolkit.py +20 -2
- camel/toolkits/networkx_toolkit.py +2 -0
- camel/toolkits/notion_toolkit.py +7 -0
- camel/toolkits/openbb_toolkit.py +2 -1
- camel/toolkits/pubmed_toolkit.py +2 -0
- camel/toolkits/reddit_toolkit.py +2 -1
- camel/toolkits/retrieval_toolkit.py +2 -1
- camel/toolkits/search_toolkit.py +2 -1
- camel/toolkits/semantic_scholar_toolkit.py +2 -0
- camel/toolkits/slack_toolkit.py +2 -0
- camel/toolkits/stripe_toolkit.py +2 -1
- camel/toolkits/sympy_toolkit.py +2 -0
- camel/toolkits/terminal_toolkit.py +2 -0
- camel/toolkits/twitter_toolkit.py +2 -1
- camel/toolkits/video_analysis_toolkit.py +2 -1
- camel/toolkits/video_download_toolkit.py +2 -1
- camel/toolkits/weather_toolkit.py +2 -0
- camel/toolkits/whatsapp_toolkit.py +2 -1
- camel/toolkits/zapier_toolkit.py +2 -1
- camel/types/enums.py +65 -0
- camel/types/unified_model_type.py +5 -0
- camel/utils/__init__.py +2 -0
- camel/utils/chunker/code_chunker.py +9 -9
- camel/utils/commons.py +50 -30
- camel/utils/constants.py +2 -2
- camel/utils/mcp.py +79 -0
- camel/verifiers/__init__.py +2 -0
- camel/verifiers/base.py +15 -15
- camel/verifiers/math_verifier.py +182 -0
- camel/verifiers/python_verifier.py +18 -26
- {camel_ai-0.2.38.dist-info → camel_ai-0.2.39.dist-info}/METADATA +3 -1
- {camel_ai-0.2.38.dist-info → camel_ai-0.2.39.dist-info}/RECORD +85 -80
- {camel_ai-0.2.38.dist-info → camel_ai-0.2.39.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.38.dist-info → camel_ai-0.2.39.dist-info}/licenses/LICENSE +0 -0
|
@@ -19,9 +19,10 @@ import requests
|
|
|
19
19
|
|
|
20
20
|
from camel.toolkits import FunctionTool
|
|
21
21
|
from camel.toolkits.base import BaseToolkit
|
|
22
|
-
from camel.utils import retry_on_error
|
|
22
|
+
from camel.utils import MCPServer, retry_on_error
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
@MCPServer()
|
|
25
26
|
class WhatsAppToolkit(BaseToolkit):
|
|
26
27
|
r"""A class representing a toolkit for WhatsApp operations.
|
|
27
28
|
|
camel/toolkits/zapier_toolkit.py
CHANGED
|
@@ -19,9 +19,10 @@ import requests
|
|
|
19
19
|
|
|
20
20
|
from camel.toolkits.base import BaseToolkit
|
|
21
21
|
from camel.toolkits.function_tool import FunctionTool
|
|
22
|
-
from camel.utils import api_keys_required, dependencies_required
|
|
22
|
+
from camel.utils import MCPServer, api_keys_required, dependencies_required
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
@MCPServer()
|
|
25
26
|
class ZapierToolkit(BaseToolkit):
|
|
26
27
|
r"""A class representing a toolkit for interacting with Zapier's NLA API.
|
|
27
28
|
|
camel/types/enums.py
CHANGED
|
@@ -217,6 +217,29 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
217
217
|
AIML_MIXTRAL_8X7B = "mistralai/Mixtral-8x7B-Instruct-v0.1"
|
|
218
218
|
AIML_MISTRAL_7B_INSTRUCT = "mistralai/Mistral-7B-Instruct-v0.1"
|
|
219
219
|
|
|
220
|
+
# ModelScope models support tool calling
|
|
221
|
+
MODELSCOPE_QWEN_2_5_7B_INSTRUCT = "Qwen/Qwen2.5-7B-Instruct"
|
|
222
|
+
MODELSCOPE_QWEN_2_5_14B_INSTRUCT = "Qwen/Qwen2.5-14B-Instruct"
|
|
223
|
+
MODELSCOPE_QWEN_2_5_32B_INSTRUCT = "Qwen/Qwen2.5-32B-Instruct"
|
|
224
|
+
MODELSCOPE_QWEN_2_5_72B_INSTRUCT = "Qwen/Qwen2.5-72B-Instruct"
|
|
225
|
+
MODELSCOPE_QWEN_2_5_CODER_7B_INSTRUCT = "Qwen/Qwen2.5-Coder-7B-Instruct"
|
|
226
|
+
MODELSCOPE_QWEN_2_5_CODER_14B_INSTRUCT = "Qwen/Qwen2.5-Coder-14B-Instruct"
|
|
227
|
+
MODELSCOPE_QWEN_2_5_CODER_32B_INSTRUCT = "Qwen/Qwen2.5-Coder-32B-Instruct"
|
|
228
|
+
MODELSCOPE_QWQ_32B = "Qwen/QwQ-32B"
|
|
229
|
+
MODELSCOPE_QWQ_32B_PREVIEW = "Qwen/QwQ-32B-Preview"
|
|
230
|
+
MODELSCOPE_LLAMA_3_1_8B_INSTRUCT = (
|
|
231
|
+
"LLM-Research/Meta-Llama-3.1-8B-Instruct"
|
|
232
|
+
)
|
|
233
|
+
MODELSCOPE_LLAMA_3_1_70B_INSTRUCT = (
|
|
234
|
+
"LLM-Research/Meta-Llama-3.1-70B-Instruct"
|
|
235
|
+
)
|
|
236
|
+
MODELSCOPE_LLAMA_3_1_405B_INSTRUCT = (
|
|
237
|
+
"LLM-Research/Meta-Llama-3.1-405B-Instruct"
|
|
238
|
+
)
|
|
239
|
+
MODELSCOPE_LLAMA_3_3_70B_INSTRUCT = "LLM-Research/Llama-3.3-70B-Instruct"
|
|
240
|
+
MODELSCOPE_MINISTRAL_8B_INSTRUCT = "mistralai/Ministral-8B-Instruct-2410"
|
|
241
|
+
MODELSCOPE_DEEPSEEK_V3_0324 = "deepseek-ai/DeepSeek-V3-0324"
|
|
242
|
+
|
|
220
243
|
def __str__(self):
|
|
221
244
|
return self.value
|
|
222
245
|
|
|
@@ -263,6 +286,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
263
286
|
self.is_sglang,
|
|
264
287
|
self.is_moonshot,
|
|
265
288
|
self.is_siliconflow,
|
|
289
|
+
self.is_modelscope,
|
|
266
290
|
self.is_zhipuai,
|
|
267
291
|
self.is_aiml,
|
|
268
292
|
self.is_azure_openai,
|
|
@@ -515,6 +539,26 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
515
539
|
ModelType.INTERNLM2_PRO_CHAT,
|
|
516
540
|
}
|
|
517
541
|
|
|
542
|
+
@property
|
|
543
|
+
def is_modelscope(self) -> bool:
|
|
544
|
+
return self in {
|
|
545
|
+
ModelType.MODELSCOPE_QWEN_2_5_7B_INSTRUCT,
|
|
546
|
+
ModelType.MODELSCOPE_QWEN_2_5_14B_INSTRUCT,
|
|
547
|
+
ModelType.MODELSCOPE_QWEN_2_5_32B_INSTRUCT,
|
|
548
|
+
ModelType.MODELSCOPE_QWEN_2_5_72B_INSTRUCT,
|
|
549
|
+
ModelType.MODELSCOPE_QWEN_2_5_CODER_7B_INSTRUCT,
|
|
550
|
+
ModelType.MODELSCOPE_QWEN_2_5_CODER_14B_INSTRUCT,
|
|
551
|
+
ModelType.MODELSCOPE_QWEN_2_5_CODER_32B_INSTRUCT,
|
|
552
|
+
ModelType.MODELSCOPE_QWQ_32B,
|
|
553
|
+
ModelType.MODELSCOPE_QWQ_32B_PREVIEW,
|
|
554
|
+
ModelType.MODELSCOPE_LLAMA_3_1_8B_INSTRUCT,
|
|
555
|
+
ModelType.MODELSCOPE_LLAMA_3_1_70B_INSTRUCT,
|
|
556
|
+
ModelType.MODELSCOPE_LLAMA_3_1_405B_INSTRUCT,
|
|
557
|
+
ModelType.MODELSCOPE_LLAMA_3_3_70B_INSTRUCT,
|
|
558
|
+
ModelType.MODELSCOPE_MINISTRAL_8B_INSTRUCT,
|
|
559
|
+
ModelType.MODELSCOPE_DEEPSEEK_V3_0324,
|
|
560
|
+
}
|
|
561
|
+
|
|
518
562
|
@property
|
|
519
563
|
def is_moonshot(self) -> bool:
|
|
520
564
|
return self in {
|
|
@@ -638,6 +682,21 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
638
682
|
ModelType.MOONSHOT_V1_32K,
|
|
639
683
|
ModelType.AIML_MIXTRAL_8X7B,
|
|
640
684
|
ModelType.AIML_MISTRAL_7B_INSTRUCT,
|
|
685
|
+
ModelType.MODELSCOPE_QWEN_2_5_7B_INSTRUCT,
|
|
686
|
+
ModelType.MODELSCOPE_QWEN_2_5_14B_INSTRUCT,
|
|
687
|
+
ModelType.MODELSCOPE_QWEN_2_5_32B_INSTRUCT,
|
|
688
|
+
ModelType.MODELSCOPE_QWEN_2_5_72B_INSTRUCT,
|
|
689
|
+
ModelType.MODELSCOPE_QWEN_2_5_CODER_7B_INSTRUCT,
|
|
690
|
+
ModelType.MODELSCOPE_QWEN_2_5_CODER_14B_INSTRUCT,
|
|
691
|
+
ModelType.MODELSCOPE_QWEN_2_5_CODER_32B_INSTRUCT,
|
|
692
|
+
ModelType.MODELSCOPE_QWQ_32B,
|
|
693
|
+
ModelType.MODELSCOPE_QWQ_32B_PREVIEW,
|
|
694
|
+
ModelType.MODELSCOPE_LLAMA_3_1_8B_INSTRUCT,
|
|
695
|
+
ModelType.MODELSCOPE_LLAMA_3_1_70B_INSTRUCT,
|
|
696
|
+
ModelType.MODELSCOPE_LLAMA_3_1_405B_INSTRUCT,
|
|
697
|
+
ModelType.MODELSCOPE_LLAMA_3_3_70B_INSTRUCT,
|
|
698
|
+
ModelType.MODELSCOPE_MINISTRAL_8B_INSTRUCT,
|
|
699
|
+
ModelType.MODELSCOPE_DEEPSEEK_V3_0324,
|
|
641
700
|
}:
|
|
642
701
|
return 32_768
|
|
643
702
|
elif self in {
|
|
@@ -922,6 +981,7 @@ class ModelPlatformType(Enum):
|
|
|
922
981
|
SGLANG = "sglang"
|
|
923
982
|
INTERNLM = "internlm"
|
|
924
983
|
MOONSHOT = "moonshot"
|
|
984
|
+
MODELSCOPE = "modelscope"
|
|
925
985
|
SILICONFLOW = "siliconflow"
|
|
926
986
|
AIML = "aiml"
|
|
927
987
|
VOLCANO = "volcano"
|
|
@@ -1050,6 +1110,11 @@ class ModelPlatformType(Enum):
|
|
|
1050
1110
|
r"""Returns whether this platform is Moonshot model."""
|
|
1051
1111
|
return self is ModelPlatformType.MOONSHOT
|
|
1052
1112
|
|
|
1113
|
+
@property
|
|
1114
|
+
def is_modelscope(self) -> bool:
|
|
1115
|
+
r"""Returns whether this platform is ModelScope model."""
|
|
1116
|
+
return self is ModelPlatformType.MODELSCOPE
|
|
1117
|
+
|
|
1053
1118
|
@property
|
|
1054
1119
|
def is_siliconflow(self) -> bool:
|
|
1055
1120
|
r"""Returns whether this platform is SiliconFlow."""
|
|
@@ -123,6 +123,11 @@ class UnifiedModelType(str):
|
|
|
123
123
|
r"""Returns whether the model is a InternLM model."""
|
|
124
124
|
return True
|
|
125
125
|
|
|
126
|
+
@property
|
|
127
|
+
def is_modelscope(self) -> bool:
|
|
128
|
+
r"""Returns whether the model is a ModelScope serverd model."""
|
|
129
|
+
return True
|
|
130
|
+
|
|
126
131
|
@property
|
|
127
132
|
def is_moonshot(self) -> bool:
|
|
128
133
|
r"""Returns whether this platform is Moonshot model."""
|
camel/utils/__init__.py
CHANGED
|
@@ -41,6 +41,7 @@ from .commons import (
|
|
|
41
41
|
)
|
|
42
42
|
from .constants import Constants
|
|
43
43
|
from .deduplication import DeduplicationResult, deduplicate_internally
|
|
44
|
+
from .mcp import MCPServer
|
|
44
45
|
from .response_format import get_pydantic_model
|
|
45
46
|
from .token_counting import (
|
|
46
47
|
AnthropicTokenCounter,
|
|
@@ -88,4 +89,5 @@ __all__ = [
|
|
|
88
89
|
"retry_on_error",
|
|
89
90
|
"BatchProcessor",
|
|
90
91
|
"with_timeout",
|
|
92
|
+
"MCPServer",
|
|
91
93
|
]
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
import re
|
|
15
|
-
from typing import List, Optional
|
|
15
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from unstructured.documents.elements import Element
|
|
18
19
|
|
|
19
20
|
from camel.utils import get_model_encoding
|
|
20
21
|
|
|
@@ -23,18 +24,15 @@ from .base import BaseChunker
|
|
|
23
24
|
|
|
24
25
|
class CodeChunker(BaseChunker):
|
|
25
26
|
r"""A class for chunking code or text while respecting structure
|
|
26
|
-
|
|
27
|
+
and token limits.
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
This class ensures that structured elements such as functions,
|
|
30
|
+
classes, and regions are not arbitrarily split across chunks.
|
|
31
|
+
It also handles oversized lines and Base64-encoded images.
|
|
31
32
|
|
|
32
33
|
Attributes:
|
|
33
34
|
chunk_size (int, optional): The maximum token size per chunk.
|
|
34
35
|
(default: :obj:`8192`)
|
|
35
|
-
token_counter (BaseTokenCounter, optional): The tokenizer used for
|
|
36
|
-
token counting, if `None`, OpenAITokenCounter will be used.
|
|
37
|
-
(default: :obj:`None`)
|
|
38
36
|
remove_image: (bool, optional): If the chunker should skip the images.
|
|
39
37
|
model_name (str, optional): The tokenizer model name used
|
|
40
38
|
for token counting. (default: :obj:`"cl100k_base"`)
|
|
@@ -108,6 +106,8 @@ class CodeChunker(BaseChunker):
|
|
|
108
106
|
Returns:
|
|
109
107
|
List[str]: A list of chunked text segments.
|
|
110
108
|
"""
|
|
109
|
+
from unstructured.documents.elements import Element, ElementMetadata
|
|
110
|
+
|
|
111
111
|
content_str = "\n".join(map(str, content))
|
|
112
112
|
chunks = []
|
|
113
113
|
current_chunk: list[str] = []
|
camel/utils/commons.py
CHANGED
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
# See the License for the specific language governing permissions and
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
import asyncio
|
|
14
15
|
import functools
|
|
15
16
|
import importlib
|
|
17
|
+
import inspect
|
|
16
18
|
import logging
|
|
17
19
|
import os
|
|
18
20
|
import platform
|
|
@@ -161,7 +163,7 @@ def get_task_list(task_response: str) -> List[str]:
|
|
|
161
163
|
|
|
162
164
|
|
|
163
165
|
def check_server_running(server_url: str) -> bool:
|
|
164
|
-
r"""Check whether the port
|
|
166
|
+
r"""Check whether the port referred by the URL to the server
|
|
165
167
|
is open.
|
|
166
168
|
|
|
167
169
|
Args:
|
|
@@ -978,36 +980,54 @@ def with_timeout(timeout=None):
|
|
|
978
980
|
"""
|
|
979
981
|
|
|
980
982
|
def decorator(func):
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
def target():
|
|
996
|
-
result_container.append(func(*args, **kwargs))
|
|
997
|
-
|
|
998
|
-
# Start the function in a new thread
|
|
999
|
-
thread = threading.Thread(target=target)
|
|
1000
|
-
thread.start()
|
|
1001
|
-
thread.join(effective_timeout)
|
|
1002
|
-
|
|
1003
|
-
# Check if the thread is still alive after the timeout
|
|
1004
|
-
if thread.is_alive():
|
|
1005
|
-
return (
|
|
1006
|
-
f"Function `{func.__name__}` execution timed out, "
|
|
1007
|
-
f"exceeded {effective_timeout} seconds."
|
|
983
|
+
if inspect.iscoroutinefunction(func):
|
|
984
|
+
|
|
985
|
+
@functools.wraps(func)
|
|
986
|
+
async def async_wrapper(*args, **kwargs):
|
|
987
|
+
eff_timeout = timeout
|
|
988
|
+
if eff_timeout is None and args:
|
|
989
|
+
eff_timeout = getattr(args[0], 'timeout', None)
|
|
990
|
+
|
|
991
|
+
if eff_timeout is None:
|
|
992
|
+
return await func(*args, **kwargs)
|
|
993
|
+
|
|
994
|
+
return await asyncio.wait_for(
|
|
995
|
+
func(*args, **kwargs), timeout=eff_timeout
|
|
1008
996
|
)
|
|
1009
|
-
|
|
1010
|
-
|
|
997
|
+
|
|
998
|
+
return async_wrapper
|
|
999
|
+
else:
|
|
1000
|
+
|
|
1001
|
+
@functools.wraps(func)
|
|
1002
|
+
def wrapper(*args, **kwargs):
|
|
1003
|
+
# Determine the effective timeout value
|
|
1004
|
+
effective_timeout = timeout
|
|
1005
|
+
if effective_timeout is None and args:
|
|
1006
|
+
effective_timeout = getattr(args[0], 'timeout', None)
|
|
1007
|
+
|
|
1008
|
+
# If no timeout value is provided, execute function normally
|
|
1009
|
+
if effective_timeout is None:
|
|
1010
|
+
return func(*args, **kwargs)
|
|
1011
|
+
|
|
1012
|
+
# Container to hold the result of the function call
|
|
1013
|
+
result_container = []
|
|
1014
|
+
|
|
1015
|
+
def target():
|
|
1016
|
+
result_container.append(func(*args, **kwargs))
|
|
1017
|
+
|
|
1018
|
+
# Start the function in a new thread
|
|
1019
|
+
thread = threading.Thread(target=target)
|
|
1020
|
+
thread.start()
|
|
1021
|
+
thread.join(effective_timeout)
|
|
1022
|
+
|
|
1023
|
+
# Check if the thread is still alive after the timeout
|
|
1024
|
+
if thread.is_alive():
|
|
1025
|
+
return (
|
|
1026
|
+
f"Function `{func.__name__}` execution timed out, "
|
|
1027
|
+
f"exceeded {effective_timeout} seconds."
|
|
1028
|
+
)
|
|
1029
|
+
else:
|
|
1030
|
+
return result_container[0]
|
|
1011
1031
|
|
|
1012
1032
|
return wrapper
|
|
1013
1033
|
|
camel/utils/constants.py
CHANGED
|
@@ -30,8 +30,8 @@ class Constants:
|
|
|
30
30
|
# Return response with json format
|
|
31
31
|
FUNC_NAME_FOR_STRUCTURED_OUTPUT = "return_json_response"
|
|
32
32
|
|
|
33
|
-
# Default top k
|
|
33
|
+
# Default top k value for RAG
|
|
34
34
|
DEFAULT_TOP_K_RESULTS = 1
|
|
35
35
|
|
|
36
|
-
# Default similarity threshold
|
|
36
|
+
# Default similarity threshold value for RAG
|
|
37
37
|
DEFAULT_SIMILARITY_THRESHOLD = 0.7
|
camel/utils/mcp.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
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 functools
|
|
15
|
+
import inspect
|
|
16
|
+
from typing import Any, Callable, Optional
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MCPServer:
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
function_names: Optional[list[str]] = None,
|
|
23
|
+
server_name: Optional[str] = None,
|
|
24
|
+
):
|
|
25
|
+
self.function_names = function_names
|
|
26
|
+
self.server_name = server_name
|
|
27
|
+
|
|
28
|
+
def make_wrapper(self, func: Callable[..., Any]) -> Callable[..., Any]:
|
|
29
|
+
if inspect.iscoroutinefunction(func):
|
|
30
|
+
|
|
31
|
+
@functools.wraps(func)
|
|
32
|
+
async def wrapper(*args, **kwargs):
|
|
33
|
+
return await func(*args, **kwargs)
|
|
34
|
+
else:
|
|
35
|
+
|
|
36
|
+
@functools.wraps(func)
|
|
37
|
+
def wrapper(*args, **kwargs):
|
|
38
|
+
return func(*args, **kwargs)
|
|
39
|
+
|
|
40
|
+
wrapper.__signature__ = inspect.signature(func) # type: ignore[attr-defined]
|
|
41
|
+
return wrapper
|
|
42
|
+
|
|
43
|
+
def __call__(self, cls):
|
|
44
|
+
from mcp.server.fastmcp import FastMCP
|
|
45
|
+
|
|
46
|
+
from camel.toolkits.base import BaseToolkit
|
|
47
|
+
|
|
48
|
+
original_init = cls.__init__
|
|
49
|
+
|
|
50
|
+
def new_init(instance, *args, **kwargs):
|
|
51
|
+
original_init(instance, *args, **kwargs)
|
|
52
|
+
self.server_name = self.server_name or cls.__name__
|
|
53
|
+
instance.mcp = FastMCP(self.server_name)
|
|
54
|
+
|
|
55
|
+
if not self.function_names and not isinstance(
|
|
56
|
+
instance, BaseToolkit
|
|
57
|
+
):
|
|
58
|
+
raise ValueError(
|
|
59
|
+
"Please specify function names or use BaseToolkit."
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
function_names = self.function_names
|
|
63
|
+
if not function_names and isinstance(instance, BaseToolkit):
|
|
64
|
+
function_names = [
|
|
65
|
+
tool.get_function_name() for tool in instance.get_tools()
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
for name in function_names:
|
|
69
|
+
func = getattr(instance, name, None)
|
|
70
|
+
if func is None or not callable(func):
|
|
71
|
+
raise ValueError(
|
|
72
|
+
f"Method {name} not found in class {cls.__name} or "
|
|
73
|
+
"cannot be called."
|
|
74
|
+
)
|
|
75
|
+
wrapper = self.make_wrapper(func)
|
|
76
|
+
instance.mcp.tool(name=name)(wrapper)
|
|
77
|
+
|
|
78
|
+
cls.__init__ = new_init
|
|
79
|
+
return cls
|
camel/verifiers/__init__.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
|
from .base import BaseVerifier
|
|
15
|
+
from .math_verifier import MathVerifier
|
|
15
16
|
from .models import VerificationOutcome
|
|
16
17
|
from .python_verifier import PythonVerifier
|
|
17
18
|
|
|
@@ -19,4 +20,5 @@ __all__ = [
|
|
|
19
20
|
"BaseVerifier",
|
|
20
21
|
"VerificationOutcome",
|
|
21
22
|
"PythonVerifier",
|
|
23
|
+
"MathVerifier",
|
|
22
24
|
]
|
camel/verifiers/base.py
CHANGED
|
@@ -163,7 +163,7 @@ class BaseVerifier(ABC):
|
|
|
163
163
|
pass
|
|
164
164
|
|
|
165
165
|
async def verify(
|
|
166
|
-
self, solution: str,
|
|
166
|
+
self, solution: str, reference_answer: Optional[str]
|
|
167
167
|
) -> VerificationResult:
|
|
168
168
|
r"""Perform verification with full error handling.
|
|
169
169
|
|
|
@@ -174,7 +174,7 @@ class BaseVerifier(ABC):
|
|
|
174
174
|
|
|
175
175
|
Args:
|
|
176
176
|
solution (str): The generated response that needs verification.
|
|
177
|
-
|
|
177
|
+
reference_answer (Optional[str]): The expected correct answer to
|
|
178
178
|
compare against.
|
|
179
179
|
|
|
180
180
|
Returns:
|
|
@@ -215,13 +215,13 @@ class BaseVerifier(ABC):
|
|
|
215
215
|
verification_result = (
|
|
216
216
|
await asyncio.wait_for(
|
|
217
217
|
self._verify_implementation(
|
|
218
|
-
verifiable_solution,
|
|
218
|
+
verifiable_solution, reference_answer
|
|
219
219
|
),
|
|
220
220
|
timeout=self._timeout,
|
|
221
221
|
)
|
|
222
222
|
if self._timeout
|
|
223
223
|
else await self._verify_implementation(
|
|
224
|
-
verifiable_solution,
|
|
224
|
+
verifiable_solution, reference_answer
|
|
225
225
|
)
|
|
226
226
|
)
|
|
227
227
|
|
|
@@ -267,7 +267,7 @@ class BaseVerifier(ABC):
|
|
|
267
267
|
|
|
268
268
|
@abstractmethod
|
|
269
269
|
async def _verify_implementation(
|
|
270
|
-
self, solution: str,
|
|
270
|
+
self, solution: str, reference_answer: Optional[str]
|
|
271
271
|
) -> VerificationResult:
|
|
272
272
|
r"""Abstract method for verification logic.
|
|
273
273
|
|
|
@@ -276,7 +276,7 @@ class BaseVerifier(ABC):
|
|
|
276
276
|
|
|
277
277
|
Args:
|
|
278
278
|
solution (str): The generated response requiring verification.
|
|
279
|
-
|
|
279
|
+
reference_answer (Optional[str]): The expected reference output.
|
|
280
280
|
|
|
281
281
|
Returns:
|
|
282
282
|
VerificationResult: Contains verification status and details.
|
|
@@ -293,7 +293,7 @@ class BaseVerifier(ABC):
|
|
|
293
293
|
async def verify_batch(
|
|
294
294
|
self,
|
|
295
295
|
solutions: List[str],
|
|
296
|
-
|
|
296
|
+
reference_answers: List[Optional[str]],
|
|
297
297
|
raise_on_error: bool = False,
|
|
298
298
|
) -> List[VerificationResult]:
|
|
299
299
|
r"""Verify multiple solutions in parallel with controlled concurrency.
|
|
@@ -305,8 +305,8 @@ class BaseVerifier(ABC):
|
|
|
305
305
|
Args:
|
|
306
306
|
solutions (List[str]): A list of generated solutions to be
|
|
307
307
|
verified.
|
|
308
|
-
|
|
309
|
-
comparison. Each element corresponds to a solution.
|
|
308
|
+
reference_answers (List[Optional[str]]): A list of expected outputs
|
|
309
|
+
for comparison. Each element corresponds to a solution.
|
|
310
310
|
raise_on_error (bool, optional): If True, raises an exception if
|
|
311
311
|
any verification fails. (default: :obj:`False`)
|
|
312
312
|
|
|
@@ -337,13 +337,13 @@ class BaseVerifier(ABC):
|
|
|
337
337
|
semaphore = asyncio.Semaphore(max(1, max_workers))
|
|
338
338
|
|
|
339
339
|
async def _verify_with_semaphore(
|
|
340
|
-
solution: str,
|
|
340
|
+
solution: str, reference_answer: Optional[str]
|
|
341
341
|
) -> VerificationResult:
|
|
342
342
|
start_time = time.time()
|
|
343
343
|
try:
|
|
344
344
|
async with semaphore:
|
|
345
345
|
verification_result = await self.verify(
|
|
346
|
-
solution,
|
|
346
|
+
solution, reference_answer
|
|
347
347
|
)
|
|
348
348
|
processing_time = time.time() - start_time
|
|
349
349
|
success = (
|
|
@@ -368,12 +368,12 @@ class BaseVerifier(ABC):
|
|
|
368
368
|
all_results: List[VerificationResult] = []
|
|
369
369
|
for i in range(0, len(solutions), batch_size):
|
|
370
370
|
batch_solutions = solutions[i : i + batch_size]
|
|
371
|
-
|
|
371
|
+
batch_reference_answers = reference_answers[i : i + batch_size]
|
|
372
372
|
|
|
373
373
|
verification_tasks = [
|
|
374
|
-
_verify_with_semaphore(solution,
|
|
375
|
-
for solution,
|
|
376
|
-
batch_solutions,
|
|
374
|
+
_verify_with_semaphore(solution, reference_answer)
|
|
375
|
+
for solution, reference_answer in zip(
|
|
376
|
+
batch_solutions, batch_reference_answers
|
|
377
377
|
)
|
|
378
378
|
]
|
|
379
379
|
try:
|