camel-ai 0.2.75a6__py3-none-any.whl → 0.2.76__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 +1001 -205
- camel/agents/mcp_agent.py +30 -27
- camel/configs/__init__.py +6 -0
- camel/configs/amd_config.py +70 -0
- camel/configs/cometapi_config.py +104 -0
- camel/data_collectors/alpaca_collector.py +15 -6
- camel/environments/tic_tac_toe.py +1 -1
- camel/interpreters/__init__.py +2 -0
- camel/interpreters/docker/Dockerfile +3 -12
- camel/interpreters/microsandbox_interpreter.py +395 -0
- camel/loaders/__init__.py +11 -2
- camel/loaders/chunkr_reader.py +9 -0
- camel/memories/__init__.py +2 -1
- camel/memories/agent_memories.py +3 -1
- camel/memories/blocks/chat_history_block.py +21 -3
- camel/memories/records.py +88 -8
- camel/messages/base.py +127 -34
- camel/models/__init__.py +4 -0
- camel/models/amd_model.py +101 -0
- camel/models/azure_openai_model.py +0 -6
- camel/models/base_model.py +30 -0
- camel/models/cometapi_model.py +83 -0
- camel/models/model_factory.py +4 -0
- camel/models/openai_compatible_model.py +0 -6
- camel/models/openai_model.py +0 -6
- camel/models/zhipuai_model.py +61 -2
- camel/parsers/__init__.py +18 -0
- camel/parsers/mcp_tool_call_parser.py +176 -0
- camel/retrievers/auto_retriever.py +1 -0
- camel/runtimes/daytona_runtime.py +11 -12
- camel/societies/workforce/prompts.py +131 -50
- camel/societies/workforce/single_agent_worker.py +434 -49
- camel/societies/workforce/structured_output_handler.py +30 -18
- camel/societies/workforce/task_channel.py +43 -0
- camel/societies/workforce/utils.py +105 -12
- camel/societies/workforce/workforce.py +1322 -311
- camel/societies/workforce/workforce_logger.py +24 -5
- camel/storages/key_value_storages/json.py +15 -2
- camel/storages/object_storages/google_cloud.py +1 -1
- camel/storages/vectordb_storages/oceanbase.py +10 -11
- camel/storages/vectordb_storages/tidb.py +8 -6
- camel/tasks/task.py +4 -3
- camel/toolkits/__init__.py +18 -5
- camel/toolkits/aci_toolkit.py +45 -0
- camel/toolkits/code_execution.py +28 -1
- camel/toolkits/context_summarizer_toolkit.py +684 -0
- camel/toolkits/dingtalk.py +1135 -0
- camel/toolkits/edgeone_pages_mcp_toolkit.py +11 -31
- camel/toolkits/{file_write_toolkit.py → file_toolkit.py} +194 -34
- camel/toolkits/function_tool.py +6 -1
- camel/toolkits/google_drive_mcp_toolkit.py +12 -31
- camel/toolkits/hybrid_browser_toolkit/config_loader.py +12 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +79 -2
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +95 -59
- camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +5 -612
- camel/toolkits/hybrid_browser_toolkit/ts/package.json +0 -1
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +619 -95
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +7 -2
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +115 -219
- camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +1 -0
- camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +39 -6
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +405 -131
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +9 -5
- camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +98 -31
- camel/toolkits/markitdown_toolkit.py +27 -1
- camel/toolkits/mcp_toolkit.py +348 -348
- camel/toolkits/message_integration.py +3 -0
- camel/toolkits/minimax_mcp_toolkit.py +195 -0
- camel/toolkits/note_taking_toolkit.py +18 -8
- camel/toolkits/notion_mcp_toolkit.py +16 -26
- camel/toolkits/origene_mcp_toolkit.py +8 -49
- camel/toolkits/playwright_mcp_toolkit.py +12 -31
- camel/toolkits/resend_toolkit.py +168 -0
- camel/toolkits/slack_toolkit.py +50 -1
- camel/toolkits/terminal_toolkit/__init__.py +18 -0
- camel/toolkits/terminal_toolkit/terminal_toolkit.py +924 -0
- camel/toolkits/terminal_toolkit/utils.py +532 -0
- camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
- camel/toolkits/video_analysis_toolkit.py +17 -11
- camel/toolkits/wechat_official_toolkit.py +483 -0
- camel/types/enums.py +124 -1
- camel/types/unified_model_type.py +5 -0
- camel/utils/commons.py +17 -0
- camel/utils/context_utils.py +804 -0
- camel/utils/mcp.py +136 -2
- camel/utils/token_counting.py +25 -17
- {camel_ai-0.2.75a6.dist-info → camel_ai-0.2.76.dist-info}/METADATA +158 -59
- {camel_ai-0.2.75a6.dist-info → camel_ai-0.2.76.dist-info}/RECORD +95 -76
- camel/loaders/pandas_reader.py +0 -368
- camel/toolkits/terminal_toolkit.py +0 -1788
- {camel_ai-0.2.75a6.dist-info → camel_ai-0.2.76.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.75a6.dist-info → camel_ai-0.2.76.dist-info}/licenses/LICENSE +0 -0
camel/utils/mcp.py
CHANGED
|
@@ -13,7 +13,121 @@
|
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
import functools
|
|
15
15
|
import inspect
|
|
16
|
-
|
|
16
|
+
import warnings
|
|
17
|
+
from typing import (
|
|
18
|
+
Any,
|
|
19
|
+
Callable,
|
|
20
|
+
List,
|
|
21
|
+
Optional,
|
|
22
|
+
Tuple,
|
|
23
|
+
Union,
|
|
24
|
+
get_args,
|
|
25
|
+
get_origin,
|
|
26
|
+
get_type_hints,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from pydantic import create_model
|
|
30
|
+
from pydantic.errors import PydanticSchemaGenerationError
|
|
31
|
+
|
|
32
|
+
from camel.logger import get_logger
|
|
33
|
+
|
|
34
|
+
logger = get_logger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _is_pydantic_serializable(type_annotation: Any) -> Tuple[bool, str]:
|
|
38
|
+
r"""Check if a type annotation is Pydantic serializable.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
type_annotation: The type annotation to check
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Tuple[bool, str]: (is_serializable, error_message)
|
|
45
|
+
"""
|
|
46
|
+
# Handle None type
|
|
47
|
+
if type_annotation is type(None) or type_annotation is None:
|
|
48
|
+
return True, ""
|
|
49
|
+
|
|
50
|
+
# Handle generic types (List, Dict, Optional, etc.)
|
|
51
|
+
origin = get_origin(type_annotation)
|
|
52
|
+
if origin is not None:
|
|
53
|
+
args = get_args(type_annotation)
|
|
54
|
+
|
|
55
|
+
# For Union types (including Optional), check all args
|
|
56
|
+
if origin is Union:
|
|
57
|
+
for arg in args:
|
|
58
|
+
is_serializable, error_msg = _is_pydantic_serializable(arg)
|
|
59
|
+
if not is_serializable:
|
|
60
|
+
return False, error_msg
|
|
61
|
+
return True, ""
|
|
62
|
+
|
|
63
|
+
# For List, Set, Tuple, etc., check the contained types
|
|
64
|
+
if origin in (list, set, tuple, frozenset):
|
|
65
|
+
for arg in args:
|
|
66
|
+
is_serializable, error_msg = _is_pydantic_serializable(arg)
|
|
67
|
+
if not is_serializable:
|
|
68
|
+
return False, error_msg
|
|
69
|
+
return True, ""
|
|
70
|
+
|
|
71
|
+
# For Dict, check both key and value types
|
|
72
|
+
if origin is dict:
|
|
73
|
+
for arg in args:
|
|
74
|
+
is_serializable, error_msg = _is_pydantic_serializable(arg)
|
|
75
|
+
if not is_serializable:
|
|
76
|
+
return False, error_msg
|
|
77
|
+
return True, ""
|
|
78
|
+
|
|
79
|
+
# Try to create a simple pydantic model with this type
|
|
80
|
+
try:
|
|
81
|
+
create_model("TestModel", test_field=(type_annotation, ...))
|
|
82
|
+
# If model creation succeeds, the type is serializable
|
|
83
|
+
return True, ""
|
|
84
|
+
except (PydanticSchemaGenerationError, TypeError, ValueError) as e:
|
|
85
|
+
error_msg = (
|
|
86
|
+
f"Type '{type_annotation}' is not Pydantic serializable. "
|
|
87
|
+
f"Consider using a custom serializable type or converting "
|
|
88
|
+
f"to bytes/base64. Error: {e!s}"
|
|
89
|
+
)
|
|
90
|
+
return False, error_msg
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _validate_function_types(func: Callable[..., Any]) -> List[str]:
|
|
94
|
+
r"""Validate function parameter and return types are Pydantic serializable.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
func (Callable[..., Any]): The function to validate.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
List[str]: List of error messages for incompatible types.
|
|
101
|
+
"""
|
|
102
|
+
errors = []
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
type_hints = get_type_hints(func)
|
|
106
|
+
except (NameError, AttributeError) as e:
|
|
107
|
+
# If we can't get type hints, skip validation
|
|
108
|
+
logger.warning(f"Could not get type hints for {func.__name__}: {e}")
|
|
109
|
+
return []
|
|
110
|
+
|
|
111
|
+
# Check return type
|
|
112
|
+
return_type = type_hints.get('return', Any)
|
|
113
|
+
if return_type != Any:
|
|
114
|
+
is_serializable, error_msg = _is_pydantic_serializable(return_type)
|
|
115
|
+
if not is_serializable:
|
|
116
|
+
errors.append(f"Return type: {error_msg}")
|
|
117
|
+
|
|
118
|
+
# Check parameter types
|
|
119
|
+
sig = inspect.signature(func)
|
|
120
|
+
for param_name, _param in sig.parameters.items():
|
|
121
|
+
if param_name == 'self':
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
param_type = type_hints.get(param_name, Any)
|
|
125
|
+
if param_type != Any:
|
|
126
|
+
is_serializable, error_msg = _is_pydantic_serializable(param_type)
|
|
127
|
+
if not is_serializable:
|
|
128
|
+
errors.append(f"Parameter '{param_name}': {error_msg}")
|
|
129
|
+
|
|
130
|
+
return errors
|
|
17
131
|
|
|
18
132
|
|
|
19
133
|
class MCPServer:
|
|
@@ -55,7 +169,7 @@ class MCPServer:
|
|
|
55
169
|
|
|
56
170
|
def __init__(
|
|
57
171
|
self,
|
|
58
|
-
function_names: Optional[
|
|
172
|
+
function_names: Optional[List[str]] = None,
|
|
59
173
|
server_name: Optional[str] = None,
|
|
60
174
|
):
|
|
61
175
|
self.function_names = function_names
|
|
@@ -135,6 +249,26 @@ class MCPServer:
|
|
|
135
249
|
f"Method {name} not found in class {cls.__name__} or "
|
|
136
250
|
"cannot be called."
|
|
137
251
|
)
|
|
252
|
+
|
|
253
|
+
# Validate function types for Pydantic compatibility
|
|
254
|
+
type_errors = _validate_function_types(func)
|
|
255
|
+
if type_errors:
|
|
256
|
+
error_message = (
|
|
257
|
+
f"Method '{name}' in class '{cls.__name__}' has "
|
|
258
|
+
f"non-Pydantic-serializable types:\n"
|
|
259
|
+
+ "\n".join(f" - {error}" for error in type_errors)
|
|
260
|
+
+ "\n\nSuggestions:"
|
|
261
|
+
+ "\n - Use standard Python types (str, int, float, bool, bytes)" # noqa: E501
|
|
262
|
+
+ "\n - Convert complex objects to JSON strings or bytes" # noqa: E501
|
|
263
|
+
+ "\n - Create custom Pydantic models for complex data" # noqa: E501
|
|
264
|
+
+ "\n - Use base64 encoding for binary data like images" # noqa: E501
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# For now, issue a warning instead of raising an error
|
|
268
|
+
# This allows gradual migration while alerting developers
|
|
269
|
+
warnings.warn(error_message, UserWarning, stacklevel=3)
|
|
270
|
+
logger.warning(error_message)
|
|
271
|
+
|
|
138
272
|
wrapper = self.make_wrapper(func)
|
|
139
273
|
instance.mcp.tool(name=name)(wrapper)
|
|
140
274
|
|
camel/utils/token_counting.py
CHANGED
|
@@ -195,24 +195,32 @@ class OpenAITokenCounter(BaseTokenCounter):
|
|
|
195
195
|
image_str: str = item["image_url"]["url"]
|
|
196
196
|
detail = item["image_url"]["detail"]
|
|
197
197
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
#
|
|
202
|
-
|
|
203
|
-
|
|
198
|
+
# Only count tokens for base64 encoded images
|
|
199
|
+
# For URLs, we cannot reliably determine token count without fetching the image
|
|
200
|
+
if image_str.startswith("data:image"):
|
|
201
|
+
# Base64 encoded image
|
|
202
|
+
image_prefix_format = "data:image/{};base64,"
|
|
203
|
+
image_prefix: Optional[str] = None
|
|
204
|
+
for image_type in list(OpenAIImageType):
|
|
205
|
+
# Find the correct image format
|
|
206
|
+
image_prefix = image_prefix_format.format(
|
|
207
|
+
image_type.value
|
|
208
|
+
)
|
|
209
|
+
if image_prefix in image_str:
|
|
210
|
+
break
|
|
211
|
+
assert isinstance(image_prefix, str)
|
|
212
|
+
encoded_image = image_str.split(image_prefix)[
|
|
213
|
+
1
|
|
214
|
+
]
|
|
215
|
+
image_bytes = BytesIO(
|
|
216
|
+
base64.b64decode(encoded_image)
|
|
204
217
|
)
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
)
|
|
212
|
-
image = Image.open(image_bytes)
|
|
213
|
-
num_tokens += self._count_tokens_from_image(
|
|
214
|
-
image, OpenAIVisionDetailType(detail)
|
|
215
|
-
)
|
|
218
|
+
image = Image.open(image_bytes)
|
|
219
|
+
num_tokens += self._count_tokens_from_image(
|
|
220
|
+
image, OpenAIVisionDetailType(detail)
|
|
221
|
+
)
|
|
222
|
+
# Note: For regular URLs, token count cannot be determined without fetching the image
|
|
223
|
+
# The actual token usage will be reported by the API response
|
|
216
224
|
if key == "name":
|
|
217
225
|
num_tokens += self.tokens_per_name
|
|
218
226
|
|