camel-ai 0.2.76a4__py3-none-any.whl → 0.2.76a5__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.

Files changed (35) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +276 -21
  3. camel/configs/__init__.py +3 -0
  4. camel/configs/cometapi_config.py +104 -0
  5. camel/interpreters/docker/Dockerfile +3 -12
  6. camel/memories/blocks/chat_history_block.py +4 -1
  7. camel/memories/records.py +52 -8
  8. camel/messages/base.py +1 -1
  9. camel/models/__init__.py +2 -0
  10. camel/models/cometapi_model.py +83 -0
  11. camel/models/model_factory.py +2 -0
  12. camel/retrievers/auto_retriever.py +1 -0
  13. camel/societies/workforce/workforce.py +9 -7
  14. camel/storages/key_value_storages/json.py +15 -2
  15. camel/storages/vectordb_storages/tidb.py +8 -6
  16. camel/toolkits/__init__.py +4 -0
  17. camel/toolkits/dingtalk.py +1135 -0
  18. camel/toolkits/edgeone_pages_mcp_toolkit.py +11 -31
  19. camel/toolkits/google_drive_mcp_toolkit.py +12 -31
  20. camel/toolkits/message_integration.py +3 -0
  21. camel/toolkits/notion_mcp_toolkit.py +16 -26
  22. camel/toolkits/origene_mcp_toolkit.py +8 -49
  23. camel/toolkits/playwright_mcp_toolkit.py +12 -31
  24. camel/toolkits/resend_toolkit.py +168 -0
  25. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  26. camel/toolkits/terminal_toolkit/terminal_toolkit.py +909 -0
  27. camel/toolkits/terminal_toolkit/utils.py +580 -0
  28. camel/types/enums.py +109 -0
  29. camel/types/unified_model_type.py +5 -0
  30. camel/utils/commons.py +2 -0
  31. {camel_ai-0.2.76a4.dist-info → camel_ai-0.2.76a5.dist-info}/METADATA +25 -6
  32. {camel_ai-0.2.76a4.dist-info → camel_ai-0.2.76a5.dist-info}/RECORD +34 -28
  33. camel/toolkits/terminal_toolkit.py +0 -1798
  34. {camel_ai-0.2.76a4.dist-info → camel_ai-0.2.76a5.dist-info}/WHEEL +0 -0
  35. {camel_ai-0.2.76a4.dist-info → camel_ai-0.2.76a5.dist-info}/licenses/LICENSE +0 -0
camel/memories/records.py CHANGED
@@ -15,8 +15,8 @@
15
15
  # Enables postponed evaluation of annotations (for string-based type hints)
16
16
  from __future__ import annotations
17
17
 
18
+ import inspect
18
19
  import time
19
- from dataclasses import asdict
20
20
  from typing import Any, ClassVar, Dict
21
21
  from uuid import UUID, uuid4
22
22
 
@@ -63,6 +63,20 @@ class MemoryRecord(BaseModel):
63
63
  "FunctionCallingMessage": FunctionCallingMessage,
64
64
  }
65
65
 
66
+ # Cache for constructor parameters (performance optimization)
67
+ _constructor_params_cache: ClassVar[Dict[str, set]] = {}
68
+
69
+ @classmethod
70
+ def _get_constructor_params(cls, message_cls) -> set:
71
+ """Get constructor parameters for a message class with caching."""
72
+ cls_name = message_cls.__name__
73
+ if cls_name not in cls._constructor_params_cache:
74
+ sig = inspect.signature(message_cls.__init__)
75
+ cls._constructor_params_cache[cls_name] = set(
76
+ sig.parameters.keys()
77
+ ) - {'self'}
78
+ return cls._constructor_params_cache[cls_name]
79
+
66
80
  @classmethod
67
81
  def from_dict(cls, record_dict: Dict[str, Any]) -> "MemoryRecord":
68
82
  r"""Reconstruct a :obj:`MemoryRecord` from the input dict.
@@ -70,14 +84,42 @@ class MemoryRecord(BaseModel):
70
84
  Args:
71
85
  record_dict(Dict[str, Any]): A dict generated by :meth:`to_dict`.
72
86
  """
87
+ from camel.types import OpenAIBackendRole, RoleType
88
+
73
89
  message_cls = cls._MESSAGE_TYPES[record_dict["message"]["__class__"]]
74
- kwargs: Dict = record_dict["message"].copy()
75
- kwargs.pop("__class__")
76
- reconstructed_message = message_cls(**kwargs)
90
+ data = record_dict["message"].copy()
91
+ data.pop("__class__")
92
+
93
+ # Convert role_type string to enum
94
+ if "role_type" in data and isinstance(data["role_type"], str):
95
+ data["role_type"] = RoleType(data["role_type"])
96
+
97
+ # Get valid constructor parameters (cached)
98
+ valid_params = cls._get_constructor_params(message_cls)
99
+
100
+ # Separate constructor args from extra fields
101
+ kwargs = {k: v for k, v in data.items() if k in valid_params}
102
+ extra_fields = {k: v for k, v in data.items() if k not in valid_params}
103
+
104
+ # Handle meta_dict properly: merge existing meta_dict with extra fields
105
+ existing_meta = kwargs.get("meta_dict", {}) or {}
106
+ if extra_fields:
107
+ # Extra fields take precedence, but preserve existing meta_dict
108
+ # structure
109
+ merged_meta = {**existing_meta, **extra_fields}
110
+ kwargs["meta_dict"] = merged_meta
111
+ elif not existing_meta:
112
+ kwargs["meta_dict"] = None
113
+
114
+ # Convert role_at_backend
115
+ role_at_backend = record_dict["role_at_backend"]
116
+ if isinstance(role_at_backend, str):
117
+ role_at_backend = OpenAIBackendRole(role_at_backend)
118
+
77
119
  return cls(
78
120
  uuid=UUID(record_dict["uuid"]),
79
- message=reconstructed_message,
80
- role_at_backend=record_dict["role_at_backend"],
121
+ message=message_cls(**kwargs),
122
+ role_at_backend=role_at_backend,
81
123
  extra_info=record_dict["extra_info"],
82
124
  timestamp=record_dict["timestamp"],
83
125
  agent_id=record_dict["agent_id"],
@@ -91,9 +133,11 @@ class MemoryRecord(BaseModel):
91
133
  "uuid": str(self.uuid),
92
134
  "message": {
93
135
  "__class__": self.message.__class__.__name__,
94
- **asdict(self.message),
136
+ **self.message.to_dict(),
95
137
  },
96
- "role_at_backend": self.role_at_backend,
138
+ "role_at_backend": self.role_at_backend.value
139
+ if hasattr(self.role_at_backend, "value")
140
+ else self.role_at_backend,
97
141
  "extra_info": self.extra_info,
98
142
  "timestamp": self.timestamp,
99
143
  "agent_id": self.agent_id,
camel/messages/base.py CHANGED
@@ -554,7 +554,7 @@ class BaseMessage:
554
554
  """
555
555
  return {
556
556
  "role_name": self.role_name,
557
- "role_type": self.role_type.name,
557
+ "role_type": self.role_type.value,
558
558
  **(self.meta_dict or {}),
559
559
  "content": self.content,
560
560
  }
camel/models/__init__.py CHANGED
@@ -19,6 +19,7 @@ from .azure_openai_model import AzureOpenAIModel
19
19
  from .base_audio_model import BaseAudioModel
20
20
  from .base_model import BaseModelBackend
21
21
  from .cohere_model import CohereModel
22
+ from .cometapi_model import CometAPIModel
22
23
  from .crynux_model import CrynuxModel
23
24
  from .deepseek_model import DeepSeekModel
24
25
  from .fish_audio_model import FishAudioModel
@@ -69,6 +70,7 @@ __all__ = [
69
70
  'StubModel',
70
71
  'ZhipuAIModel',
71
72
  'CohereModel',
73
+ 'CometAPIModel',
72
74
  'ModelFactory',
73
75
  'ModelManager',
74
76
  'LiteLLMModel',
@@ -0,0 +1,83 @@
1
+ # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+ import os
15
+ from typing import Any, Dict, Optional, Union
16
+
17
+ from camel.configs import CometAPIConfig
18
+ from camel.models.openai_compatible_model import OpenAICompatibleModel
19
+ from camel.types import ModelType
20
+ from camel.utils import (
21
+ BaseTokenCounter,
22
+ api_keys_required,
23
+ )
24
+
25
+
26
+ class CometAPIModel(OpenAICompatibleModel):
27
+ r"""LLM API served by CometAPI in a unified OpenAICompatibleModel
28
+ interface.
29
+
30
+ Args:
31
+ model_type (Union[ModelType, str]): Model for which a backend is
32
+ created.
33
+ model_config_dict (Optional[Dict[str, Any]], optional): A dictionary
34
+ that will be fed into:obj:`openai.ChatCompletion.create()`.
35
+ If:obj:`None`, :obj:`CometAPIConfig().as_dict()` will be used.
36
+ (default: :obj:`None`)
37
+ api_key (Optional[str], optional): The API key for authenticating
38
+ with the CometAPI service. (default: :obj:`None`).
39
+ url (Optional[str], optional): The url to the CometAPI service.
40
+ (default: :obj:`None`)
41
+ token_counter (Optional[BaseTokenCounter], optional): Token counter to
42
+ use for the model. If not provided, :obj:`OpenAITokenCounter(
43
+ ModelType.GPT_4O_MINI)` will be used.
44
+ (default: :obj:`None`)
45
+ timeout (Optional[float], optional): The timeout value in seconds for
46
+ API calls. If not provided, will fall back to the MODEL_TIMEOUT
47
+ environment variable or default to 180 seconds.
48
+ (default: :obj:`None`)
49
+ max_retries (int, optional): Maximum number of retries for API calls.
50
+ (default: :obj:`3`)
51
+ **kwargs (Any): Additional arguments to pass to the client
52
+ initialization.
53
+ """
54
+
55
+ @api_keys_required([("api_key", "COMETAPI_KEY")])
56
+ def __init__(
57
+ self,
58
+ model_type: Union[ModelType, str],
59
+ model_config_dict: Optional[Dict[str, Any]] = None,
60
+ api_key: Optional[str] = None,
61
+ url: Optional[str] = None,
62
+ token_counter: Optional[BaseTokenCounter] = None,
63
+ timeout: Optional[float] = None,
64
+ max_retries: int = 3,
65
+ **kwargs: Any,
66
+ ) -> None:
67
+ if model_config_dict is None:
68
+ model_config_dict = CometAPIConfig().as_dict()
69
+ api_key = api_key or os.environ.get("COMETAPI_KEY")
70
+ url = url or os.environ.get(
71
+ "COMETAPI_API_BASE_URL", "https://api.cometapi.com/v1"
72
+ )
73
+ timeout = timeout or float(os.environ.get("MODEL_TIMEOUT", 180))
74
+ super().__init__(
75
+ model_type=model_type,
76
+ model_config_dict=model_config_dict,
77
+ api_key=api_key,
78
+ url=url,
79
+ token_counter=token_counter,
80
+ timeout=timeout,
81
+ max_retries=max_retries,
82
+ **kwargs,
83
+ )
@@ -22,6 +22,7 @@ from camel.models.aws_bedrock_model import AWSBedrockModel
22
22
  from camel.models.azure_openai_model import AzureOpenAIModel
23
23
  from camel.models.base_model import BaseModelBackend
24
24
  from camel.models.cohere_model import CohereModel
25
+ from camel.models.cometapi_model import CometAPIModel
25
26
  from camel.models.crynux_model import CrynuxModel
26
27
  from camel.models.deepseek_model import DeepSeekModel
27
28
  from camel.models.gemini_model import GeminiModel
@@ -86,6 +87,7 @@ class ModelFactory:
86
87
  ModelPlatformType.AZURE: AzureOpenAIModel,
87
88
  ModelPlatformType.ANTHROPIC: AnthropicModel,
88
89
  ModelPlatformType.GROQ: GroqModel,
90
+ ModelPlatformType.COMETAPI: CometAPIModel,
89
91
  ModelPlatformType.NEBIUS: NebiusModel,
90
92
  ModelPlatformType.LMSTUDIO: LMStudioModel,
91
93
  ModelPlatformType.OPENROUTER: OpenRouterModel,
@@ -97,6 +97,7 @@ class AutoRetriever:
97
97
  "URL (database url) and API key required for TiDB storage "
98
98
  "are not provided. Format: "
99
99
  "mysql+pymysql://<username>:<password>@<host>:4000/test"
100
+ "You can get the database url from https://tidbcloud.com/console/clusters"
100
101
  )
101
102
  return TiDBStorage(
102
103
  vector_dim=self.embedding_model.get_output_dim(),
@@ -23,6 +23,7 @@ from collections import deque
23
23
  from enum import Enum
24
24
  from typing import (
25
25
  Any,
26
+ Callable,
26
27
  Coroutine,
27
28
  Deque,
28
29
  Dict,
@@ -32,6 +33,7 @@ from typing import (
32
33
  Set,
33
34
  Tuple,
34
35
  Union,
36
+ cast,
35
37
  )
36
38
 
37
39
  from colorama import Fore
@@ -71,6 +73,7 @@ from camel.tasks.task import (
71
73
  )
72
74
  from camel.toolkits import (
73
75
  CodeExecutionToolkit,
76
+ FunctionTool,
74
77
  SearchToolkit,
75
78
  TaskPlanningToolkit,
76
79
  ThinkingToolkit,
@@ -345,10 +348,7 @@ class Workforce(BaseNode):
345
348
  None,
346
349
  ),
347
350
  output_language=coordinator_agent.output_language,
348
- tools=[
349
- tool.func
350
- for tool in coordinator_agent._internal_tools.values()
351
- ],
351
+ tools=list(coordinator_agent._internal_tools.values()),
352
352
  external_tools=[
353
353
  schema
354
354
  for schema in coordinator_agent._external_tool_schemas.values() # noqa: E501
@@ -398,9 +398,11 @@ class Workforce(BaseNode):
398
398
 
399
399
  # Since ChatAgent constructor uses a dictionary with
400
400
  # function names as keys, we don't need to manually deduplicate.
401
- combined_tools = [
402
- tool.func for tool in task_agent._internal_tools.values()
403
- ] + [tool.func for tool in task_planning_tools]
401
+ combined_tools: List[Union[FunctionTool, Callable]] = cast(
402
+ List[Union[FunctionTool, Callable]],
403
+ list(task_agent._internal_tools.values())
404
+ + task_planning_tools,
405
+ )
404
406
 
405
407
  # Create a new agent with the provided agent's configuration
406
408
  # but with the combined system message and tools
@@ -17,6 +17,8 @@ from enum import EnumMeta
17
17
  from pathlib import Path
18
18
  from typing import Any, ClassVar, Dict, List, Optional
19
19
 
20
+ from pydantic import BaseModel
21
+
20
22
  from camel.storages.key_value_storages import BaseKeyValueStorage
21
23
  from camel.types import (
22
24
  ModelType,
@@ -27,8 +29,13 @@ from camel.types import (
27
29
 
28
30
 
29
31
  class CamelJSONEncoder(json.JSONEncoder):
30
- r"""A custom JSON encoder for serializing specifically enumerated types.
31
- Ensures enumerated types can be stored in and retrieved from JSON format.
32
+ r"""A custom JSON encoder for serializing CAMEL-specific types.
33
+
34
+ Handles serialization of:
35
+ - Enumerated types (RoleType, TaskType, ModelType, OpenAIBackendRole)
36
+ - Pydantic BaseModel objects (from structured outputs)
37
+
38
+ Ensures these types can be stored in and retrieved from JSON format.
32
39
  """
33
40
 
34
41
  CAMEL_ENUMS: ClassVar[Dict[str, EnumMeta]] = {
@@ -39,8 +46,14 @@ class CamelJSONEncoder(json.JSONEncoder):
39
46
  }
40
47
 
41
48
  def default(self, obj) -> Any:
49
+ # Handle CAMEL enum types
42
50
  if type(obj) in self.CAMEL_ENUMS.values():
43
51
  return {"__enum__": str(obj)}
52
+
53
+ # Handle Pydantic BaseModel objects (e.g., from structured outputs)
54
+ if isinstance(obj, BaseModel):
55
+ return obj.model_dump()
56
+
44
57
  # Let the base class default method raise the TypeError
45
58
  return json.JSONEncoder.default(self, obj)
46
59
 
@@ -44,7 +44,7 @@ class TiDBStorage(BaseVectorStorage):
44
44
  r"""An implementation of the `BaseVectorStorage` for interacting with TiDB.
45
45
 
46
46
  The detailed information about TiDB is available at:
47
- `TiDB Vector Search <https://ai.pingcap.com/>`_
47
+ `TiDB Vector Search <https://pingcap.com/ai>`_
48
48
 
49
49
  Args:
50
50
  vector_dim (int): The dimension of storing vectors.
@@ -107,10 +107,10 @@ class TiDBStorage(BaseVectorStorage):
107
107
  )
108
108
 
109
109
  def _get_table_model(self, collection_name: str) -> Any:
110
+ from pytidb.datatype import JSON
110
111
  from pytidb.schema import Field, TableModel, VectorField
111
- from sqlalchemy import JSON
112
112
 
113
- class VectorDBRecord(TableModel):
113
+ class VectorDBRecordBase(TableModel, table=False):
114
114
  id: Optional[str] = Field(None, primary_key=True)
115
115
  vector: list[float] = VectorField(self.vector_dim)
116
116
  payload: Optional[dict[str, Any]] = Field(None, sa_type=JSON)
@@ -119,7 +119,7 @@ class TiDBStorage(BaseVectorStorage):
119
119
  # class names.
120
120
  return type(
121
121
  f"VectorDBRecord_{collection_name}",
122
- (VectorDBRecord,),
122
+ (VectorDBRecordBase,),
123
123
  {"__tablename__": collection_name},
124
124
  table=True,
125
125
  )
@@ -128,8 +128,9 @@ class TiDBStorage(BaseVectorStorage):
128
128
  r"""Opens an existing table or creates a new table in TiDB."""
129
129
  table = self._client.open_table(self.collection_name)
130
130
  if table is None:
131
+ table_model = self._get_table_model(self.collection_name)
131
132
  table = self._client.create_table(
132
- schema=self._get_table_model(self.collection_name)
133
+ schema=table_model, if_exists="skip"
133
134
  )
134
135
  return table
135
136
 
@@ -166,6 +167,7 @@ class TiDBStorage(BaseVectorStorage):
166
167
  table.
167
168
  """
168
169
  vector_count = self._table.rows()
170
+
169
171
  # Get vector dimension from table schema
170
172
  columns = self._table.columns()
171
173
  dim_value = None
@@ -303,7 +305,7 @@ class TiDBStorage(BaseVectorStorage):
303
305
  for row in rows:
304
306
  query_results.append(
305
307
  VectorDBQueryResult.create(
306
- similarity=float(row['similarity_score']),
308
+ similarity=float(row['_score']),
307
309
  id=str(row['id']),
308
310
  payload=row['payload'],
309
311
  vector=row['vector'],
@@ -41,6 +41,7 @@ from .arxiv_toolkit import ArxivToolkit
41
41
  from .slack_toolkit import SlackToolkit
42
42
  from .whatsapp_toolkit import WhatsAppToolkit
43
43
  from .wechat_official_toolkit import WeChatOfficialToolkit
44
+ from .dingtalk import DingtalkToolkit
44
45
  from .twitter_toolkit import TwitterToolkit
45
46
  from .open_api_toolkit import OpenAPIToolkit
46
47
  from .retrieval_toolkit import RetrievalToolkit
@@ -76,6 +77,7 @@ from .klavis_toolkit import KlavisToolkit
76
77
  from .aci_toolkit import ACIToolkit
77
78
  from .origene_mcp_toolkit import OrigeneToolkit
78
79
  from .playwright_mcp_toolkit import PlaywrightMCPToolkit
80
+ from .resend_toolkit import ResendToolkit
79
81
  from .wolfram_alpha_toolkit import WolframAlphaToolkit
80
82
  from .task_planning_toolkit import TaskPlanningToolkit
81
83
  from .hybrid_browser_toolkit import HybridBrowserToolkit
@@ -107,6 +109,7 @@ __all__ = [
107
109
  'SlackToolkit',
108
110
  'WhatsAppToolkit',
109
111
  'WeChatOfficialToolkit',
112
+ 'DingtalkToolkit',
110
113
  'ImageGenToolkit',
111
114
  'TwitterToolkit',
112
115
  'WeatherToolkit',
@@ -155,6 +158,7 @@ __all__ = [
155
158
  'KlavisToolkit',
156
159
  'ACIToolkit',
157
160
  'PlaywrightMCPToolkit',
161
+ 'ResendToolkit',
158
162
  'WolframAlphaToolkit',
159
163
  'BohriumToolkit',
160
164
  'OpenAIImageToolkit', # Backward compatibility