camel-ai 0.2.67__py3-none-any.whl → 0.2.80a2__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.
Files changed (224) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/_types.py +6 -2
  3. camel/agents/_utils.py +38 -0
  4. camel/agents/chat_agent.py +4014 -410
  5. camel/agents/mcp_agent.py +30 -27
  6. camel/agents/repo_agent.py +2 -1
  7. camel/benchmarks/browsecomp.py +6 -6
  8. camel/configs/__init__.py +15 -0
  9. camel/configs/aihubmix_config.py +88 -0
  10. camel/configs/amd_config.py +70 -0
  11. camel/configs/cometapi_config.py +104 -0
  12. camel/configs/minimax_config.py +93 -0
  13. camel/configs/nebius_config.py +103 -0
  14. camel/configs/vllm_config.py +2 -0
  15. camel/data_collectors/alpaca_collector.py +15 -6
  16. camel/datagen/self_improving_cot.py +1 -1
  17. camel/datasets/base_generator.py +39 -10
  18. camel/environments/__init__.py +12 -0
  19. camel/environments/rlcards_env.py +860 -0
  20. camel/environments/single_step.py +28 -3
  21. camel/environments/tic_tac_toe.py +1 -1
  22. camel/interpreters/__init__.py +2 -0
  23. camel/interpreters/docker/Dockerfile +4 -16
  24. camel/interpreters/docker_interpreter.py +3 -2
  25. camel/interpreters/e2b_interpreter.py +34 -1
  26. camel/interpreters/internal_python_interpreter.py +51 -2
  27. camel/interpreters/microsandbox_interpreter.py +395 -0
  28. camel/loaders/__init__.py +11 -2
  29. camel/loaders/base_loader.py +85 -0
  30. camel/loaders/chunkr_reader.py +9 -0
  31. camel/loaders/firecrawl_reader.py +4 -4
  32. camel/logger.py +1 -1
  33. camel/memories/agent_memories.py +84 -1
  34. camel/memories/base.py +34 -0
  35. camel/memories/blocks/chat_history_block.py +122 -4
  36. camel/memories/blocks/vectordb_block.py +8 -1
  37. camel/memories/context_creators/score_based.py +29 -237
  38. camel/memories/records.py +88 -8
  39. camel/messages/base.py +166 -40
  40. camel/messages/func_message.py +32 -5
  41. camel/models/__init__.py +10 -0
  42. camel/models/aihubmix_model.py +83 -0
  43. camel/models/aiml_model.py +1 -16
  44. camel/models/amd_model.py +101 -0
  45. camel/models/anthropic_model.py +117 -18
  46. camel/models/aws_bedrock_model.py +2 -33
  47. camel/models/azure_openai_model.py +205 -91
  48. camel/models/base_audio_model.py +3 -1
  49. camel/models/base_model.py +189 -24
  50. camel/models/cohere_model.py +5 -17
  51. camel/models/cometapi_model.py +83 -0
  52. camel/models/crynux_model.py +1 -16
  53. camel/models/deepseek_model.py +6 -16
  54. camel/models/fish_audio_model.py +6 -0
  55. camel/models/gemini_model.py +71 -20
  56. camel/models/groq_model.py +1 -17
  57. camel/models/internlm_model.py +1 -16
  58. camel/models/litellm_model.py +49 -32
  59. camel/models/lmstudio_model.py +1 -17
  60. camel/models/minimax_model.py +83 -0
  61. camel/models/mistral_model.py +1 -16
  62. camel/models/model_factory.py +27 -1
  63. camel/models/model_manager.py +24 -6
  64. camel/models/modelscope_model.py +1 -16
  65. camel/models/moonshot_model.py +185 -19
  66. camel/models/nebius_model.py +83 -0
  67. camel/models/nemotron_model.py +0 -5
  68. camel/models/netmind_model.py +1 -16
  69. camel/models/novita_model.py +1 -16
  70. camel/models/nvidia_model.py +1 -16
  71. camel/models/ollama_model.py +4 -19
  72. camel/models/openai_compatible_model.py +171 -46
  73. camel/models/openai_model.py +205 -77
  74. camel/models/openrouter_model.py +1 -17
  75. camel/models/ppio_model.py +1 -16
  76. camel/models/qianfan_model.py +1 -16
  77. camel/models/qwen_model.py +1 -16
  78. camel/models/reka_model.py +1 -16
  79. camel/models/samba_model.py +34 -47
  80. camel/models/sglang_model.py +64 -31
  81. camel/models/siliconflow_model.py +1 -16
  82. camel/models/stub_model.py +0 -4
  83. camel/models/togetherai_model.py +1 -16
  84. camel/models/vllm_model.py +1 -16
  85. camel/models/volcano_model.py +0 -17
  86. camel/models/watsonx_model.py +1 -16
  87. camel/models/yi_model.py +1 -16
  88. camel/models/zhipuai_model.py +60 -16
  89. camel/parsers/__init__.py +18 -0
  90. camel/parsers/mcp_tool_call_parser.py +176 -0
  91. camel/retrievers/auto_retriever.py +1 -0
  92. camel/runtimes/configs.py +11 -11
  93. camel/runtimes/daytona_runtime.py +15 -16
  94. camel/runtimes/docker_runtime.py +6 -6
  95. camel/runtimes/remote_http_runtime.py +5 -5
  96. camel/services/agent_openapi_server.py +380 -0
  97. camel/societies/__init__.py +2 -0
  98. camel/societies/role_playing.py +26 -28
  99. camel/societies/workforce/__init__.py +2 -0
  100. camel/societies/workforce/events.py +122 -0
  101. camel/societies/workforce/prompts.py +249 -38
  102. camel/societies/workforce/role_playing_worker.py +82 -20
  103. camel/societies/workforce/single_agent_worker.py +634 -34
  104. camel/societies/workforce/structured_output_handler.py +512 -0
  105. camel/societies/workforce/task_channel.py +169 -23
  106. camel/societies/workforce/utils.py +176 -9
  107. camel/societies/workforce/worker.py +77 -23
  108. camel/societies/workforce/workflow_memory_manager.py +772 -0
  109. camel/societies/workforce/workforce.py +3168 -478
  110. camel/societies/workforce/workforce_callback.py +74 -0
  111. camel/societies/workforce/workforce_logger.py +203 -175
  112. camel/societies/workforce/workforce_metrics.py +33 -0
  113. camel/storages/__init__.py +4 -0
  114. camel/storages/key_value_storages/json.py +15 -2
  115. camel/storages/key_value_storages/mem0_cloud.py +48 -47
  116. camel/storages/object_storages/google_cloud.py +1 -1
  117. camel/storages/vectordb_storages/__init__.py +6 -0
  118. camel/storages/vectordb_storages/chroma.py +731 -0
  119. camel/storages/vectordb_storages/oceanbase.py +13 -13
  120. camel/storages/vectordb_storages/pgvector.py +349 -0
  121. camel/storages/vectordb_storages/qdrant.py +3 -3
  122. camel/storages/vectordb_storages/surreal.py +365 -0
  123. camel/storages/vectordb_storages/tidb.py +8 -6
  124. camel/tasks/task.py +244 -27
  125. camel/toolkits/__init__.py +46 -8
  126. camel/toolkits/aci_toolkit.py +64 -19
  127. camel/toolkits/arxiv_toolkit.py +6 -6
  128. camel/toolkits/base.py +63 -5
  129. camel/toolkits/code_execution.py +28 -1
  130. camel/toolkits/context_summarizer_toolkit.py +684 -0
  131. camel/toolkits/craw4ai_toolkit.py +93 -0
  132. camel/toolkits/dappier_toolkit.py +10 -6
  133. camel/toolkits/dingtalk.py +1135 -0
  134. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  135. camel/toolkits/excel_toolkit.py +901 -67
  136. camel/toolkits/file_toolkit.py +1402 -0
  137. camel/toolkits/function_tool.py +30 -6
  138. camel/toolkits/github_toolkit.py +107 -20
  139. camel/toolkits/gmail_toolkit.py +1839 -0
  140. camel/toolkits/google_calendar_toolkit.py +38 -4
  141. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  142. camel/toolkits/human_toolkit.py +34 -10
  143. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  144. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  145. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  146. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
  147. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  148. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +3749 -0
  149. camel/toolkits/hybrid_browser_toolkit/ts/package.json +32 -0
  150. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  151. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1815 -0
  152. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  153. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +590 -0
  154. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  155. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  156. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  157. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  158. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +130 -0
  159. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +26 -0
  160. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
  161. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1032 -0
  162. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  163. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  164. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  165. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  166. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  167. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  168. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  169. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  170. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  171. camel/toolkits/image_generation_toolkit.py +390 -0
  172. camel/toolkits/jina_reranker_toolkit.py +3 -4
  173. camel/toolkits/klavis_toolkit.py +5 -1
  174. camel/toolkits/markitdown_toolkit.py +104 -0
  175. camel/toolkits/math_toolkit.py +64 -10
  176. camel/toolkits/mcp_toolkit.py +370 -45
  177. camel/toolkits/memory_toolkit.py +5 -1
  178. camel/toolkits/message_agent_toolkit.py +608 -0
  179. camel/toolkits/message_integration.py +724 -0
  180. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  181. camel/toolkits/note_taking_toolkit.py +277 -0
  182. camel/toolkits/notion_mcp_toolkit.py +224 -0
  183. camel/toolkits/openbb_toolkit.py +5 -1
  184. camel/toolkits/origene_mcp_toolkit.py +56 -0
  185. camel/toolkits/playwright_mcp_toolkit.py +12 -31
  186. camel/toolkits/pptx_toolkit.py +25 -12
  187. camel/toolkits/resend_toolkit.py +168 -0
  188. camel/toolkits/screenshot_toolkit.py +213 -0
  189. camel/toolkits/search_toolkit.py +437 -142
  190. camel/toolkits/slack_toolkit.py +104 -50
  191. camel/toolkits/sympy_toolkit.py +1 -1
  192. camel/toolkits/task_planning_toolkit.py +3 -3
  193. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  194. camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
  195. camel/toolkits/terminal_toolkit/utils.py +532 -0
  196. camel/toolkits/thinking_toolkit.py +1 -1
  197. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  198. camel/toolkits/video_analysis_toolkit.py +106 -26
  199. camel/toolkits/video_download_toolkit.py +17 -14
  200. camel/toolkits/web_deploy_toolkit.py +1219 -0
  201. camel/toolkits/wechat_official_toolkit.py +483 -0
  202. camel/toolkits/zapier_toolkit.py +5 -1
  203. camel/types/__init__.py +2 -2
  204. camel/types/agents/tool_calling_record.py +4 -1
  205. camel/types/enums.py +316 -40
  206. camel/types/openai_types.py +2 -2
  207. camel/types/unified_model_type.py +31 -4
  208. camel/utils/commons.py +36 -5
  209. camel/utils/constants.py +3 -0
  210. camel/utils/context_utils.py +1003 -0
  211. camel/utils/mcp.py +138 -4
  212. camel/utils/mcp_client.py +45 -1
  213. camel/utils/message_summarizer.py +148 -0
  214. camel/utils/token_counting.py +43 -20
  215. camel/utils/tool_result.py +44 -0
  216. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +296 -85
  217. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +219 -146
  218. camel/loaders/pandas_reader.py +0 -368
  219. camel/toolkits/dalle_toolkit.py +0 -175
  220. camel/toolkits/file_write_toolkit.py +0 -444
  221. camel/toolkits/openai_agent_toolkit.py +0 -135
  222. camel/toolkits/terminal_toolkit.py +0 -1037
  223. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
  224. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,85 @@
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 abc import ABC, abstractmethod
15
+ from pathlib import Path
16
+ from typing import Any, Dict, List, Union
17
+
18
+
19
+ class BaseLoader(ABC):
20
+ r"""Abstract base class for all data loaders in CAMEL."""
21
+
22
+ @abstractmethod
23
+ def _load_single(self, source: Union[str, Path]) -> Dict[str, Any]:
24
+ r"""Load data from a single source.
25
+
26
+ Args:
27
+ source (Union[str, Path]): The data source to load from.
28
+
29
+ Returns:
30
+ Dict[str, Any]: A dictionary containing the loaded data. It is
31
+ recommended that the dictionary includes a "content" key with
32
+ the primary data and optional metadata keys.
33
+ """
34
+ pass
35
+
36
+ def load(
37
+ self,
38
+ source: Union[str, Path, List[Union[str, Path]]],
39
+ ) -> Dict[str, List[Dict[str, Any]]]:
40
+ r"""Load data from one or multiple sources.
41
+
42
+ Args:
43
+ source (Union[str, Path, List[Union[str, Path]]]): The data source
44
+ (s) to load from. Can be:
45
+ - A single path/URL (str or Path)
46
+ - A list of paths/URLs
47
+
48
+ Returns:
49
+ Dict[str, List[Dict[str, Any]]]: A dictionary with a single key
50
+ "contents" containing a list of loaded data. If a single source
51
+ is provided, the list will contain a single item.
52
+
53
+ Raises:
54
+ ValueError: If no sources are provided
55
+ Exception: If loading fails for any source
56
+ """
57
+ if not source:
58
+ raise ValueError("At least one source must be provided")
59
+
60
+ # Convert single source to list for uniform processing
61
+ sources = [source] if isinstance(source, (str, Path)) else list(source)
62
+
63
+ # Process all sources
64
+ results = []
65
+ for i, src in enumerate(sources, 1):
66
+ try:
67
+ content = self._load_single(src)
68
+ results.append(content)
69
+ except Exception as e:
70
+ raise RuntimeError(
71
+ f"Error loading source {i}/{len(sources)}: {src}"
72
+ ) from e
73
+
74
+ return {"contents": results}
75
+
76
+ @property
77
+ @abstractmethod
78
+ def supported_formats(self) -> set[str]:
79
+ r"""Get the set of supported file formats or data sources.
80
+
81
+ Returns:
82
+ set[str]: A set of strings representing the supported formats/
83
+ sources.
84
+ """
85
+ pass
@@ -34,6 +34,12 @@ class ChunkrReaderConfig:
34
34
  high_resolution (bool, optional): Whether to use high resolution OCR.
35
35
  (default: :obj:`True`)
36
36
  ocr_strategy (str, optional): The OCR strategy. Defaults to 'Auto'.
37
+ **kwargs: Additional keyword arguments to pass to the Chunkr
38
+ Configuration. This accepts all other Configuration parameters
39
+ such as expires_in, pipeline, segment_processing,
40
+ segmentation_strategy, etc.
41
+ See: https://github.com/lumina-ai-inc/chunkr/blob/main/core/src/
42
+ models/task.rs#L749
37
43
  """
38
44
 
39
45
  def __init__(
@@ -41,10 +47,12 @@ class ChunkrReaderConfig:
41
47
  chunk_processing: int = 512,
42
48
  high_resolution: bool = True,
43
49
  ocr_strategy: str = "Auto",
50
+ **kwargs,
44
51
  ):
45
52
  self.chunk_processing = chunk_processing
46
53
  self.high_resolution = high_resolution
47
54
  self.ocr_strategy = ocr_strategy
55
+ self.kwargs = kwargs
48
56
 
49
57
 
50
58
  class ChunkrReader:
@@ -190,4 +198,5 @@ class ChunkrReader:
190
198
  "Auto": OcrStrategy.AUTO,
191
199
  "All": OcrStrategy.ALL,
192
200
  }.get(chunkr_config.ocr_strategy, OcrStrategy.ALL),
201
+ **chunkr_config.kwargs,
193
202
  )
@@ -98,8 +98,8 @@ class Firecrawl:
98
98
  def scrape(
99
99
  self,
100
100
  url: str,
101
- params: Optional[Dict[str, Any]] = None,
102
- ) -> Dict:
101
+ params: Optional[Dict[str, str]] = None,
102
+ ) -> Dict[str, str]:
103
103
  r"""To scrape a single URL. This function supports advanced scraping
104
104
  by setting different parameters and returns the full scraped data as a
105
105
  dictionary.
@@ -108,11 +108,11 @@ class Firecrawl:
108
108
 
109
109
  Args:
110
110
  url (str): The URL to read.
111
- params (Optional[Dict[str, Any]]): Additional parameters for the
111
+ params (Optional[Dict[str, str]]): Additional parameters for the
112
112
  scrape request.
113
113
 
114
114
  Returns:
115
- Dict: The scraped data.
115
+ Dict[str, str]: The scraped data.
116
116
 
117
117
  Raises:
118
118
  RuntimeError: If the scrape process fails.
camel/logger.py CHANGED
@@ -155,7 +155,7 @@ def set_log_level(level):
155
155
  _logger.debug(f"Logging level set to: {logging.getLevelName(level)}")
156
156
 
157
157
 
158
- def get_logger(name):
158
+ def get_logger(name: str):
159
159
  r"""Get a logger with the specified name, prefixed with 'camel.'.
160
160
 
161
161
 
@@ -51,7 +51,9 @@ class ChatHistoryMemory(AgentMemory):
51
51
  raise ValueError("`window_size` must be non-negative.")
52
52
  self._context_creator = context_creator
53
53
  self._window_size = window_size
54
- self._chat_history_block = ChatHistoryBlock(storage=storage)
54
+ self._chat_history_block = ChatHistoryBlock(
55
+ storage=storage,
56
+ )
55
57
  self._agent_id = agent_id
56
58
 
57
59
  @property
@@ -88,6 +90,63 @@ class ChatHistoryMemory(AgentMemory):
88
90
  def clear(self) -> None:
89
91
  self._chat_history_block.clear()
90
92
 
93
+ def clean_tool_calls(self) -> None:
94
+ r"""Removes tool call messages from memory.
95
+ This method removes all FUNCTION/TOOL role messages and any ASSISTANT
96
+ messages that contain tool_calls in their meta_dict to save token
97
+ usage.
98
+ """
99
+ from camel.types import OpenAIBackendRole
100
+
101
+ # Get all messages from storage
102
+ record_dicts = self._chat_history_block.storage.load()
103
+ if not record_dicts:
104
+ return
105
+
106
+ # Track indices to remove (reverse order for efficient deletion)
107
+ indices_to_remove = []
108
+
109
+ # Identify indices of tool-related messages
110
+ for i, record in enumerate(record_dicts):
111
+ role = record.get('role_at_backend')
112
+
113
+ # Mark FUNCTION messages for removal
114
+ if role == OpenAIBackendRole.FUNCTION.value:
115
+ indices_to_remove.append(i)
116
+ # Mark TOOL messages for removal
117
+ elif role == OpenAIBackendRole.TOOL.value:
118
+ indices_to_remove.append(i)
119
+ # Mark ASSISTANT messages with tool_calls for removal
120
+ elif role == OpenAIBackendRole.ASSISTANT.value:
121
+ message_dict = record.get('message', {})
122
+ # Check for tool_calls in message
123
+ has_tool_calls = 'tool_calls' in message_dict
124
+ is_func_calling = (
125
+ message_dict.get('__class__') == 'FunctionCallingMessage'
126
+ and 'args' in message_dict
127
+ )
128
+
129
+ if has_tool_calls or is_func_calling:
130
+ indices_to_remove.append(i)
131
+
132
+ # Remove records in-place
133
+ for i in reversed(indices_to_remove):
134
+ del record_dicts[i]
135
+
136
+ # Clear storage and save the modified records back
137
+ self._chat_history_block.storage.clear()
138
+ self._chat_history_block.storage.save(record_dicts)
139
+
140
+ def pop_records(self, count: int) -> List[MemoryRecord]:
141
+ r"""Removes the most recent records from chat history memory."""
142
+ return self._chat_history_block.pop_records(count)
143
+
144
+ def remove_records_by_indices(
145
+ self, indices: List[int]
146
+ ) -> List[MemoryRecord]:
147
+ r"""Removes records at specified indices from chat history memory."""
148
+ return self._chat_history_block.remove_records_by_indices(indices)
149
+
91
150
 
92
151
  class VectorDBMemory(AgentMemory):
93
152
  r"""An agent memory wrapper of :obj:`VectorDBBlock`. This memory queries
@@ -152,6 +211,20 @@ class VectorDBMemory(AgentMemory):
152
211
  r"""Removes all records from the vector database memory."""
153
212
  self._vectordb_block.clear()
154
213
 
214
+ def pop_records(self, count: int) -> List[MemoryRecord]:
215
+ r"""Rolling back is unsupported for vector database memory."""
216
+ raise NotImplementedError(
217
+ "VectorDBMemory does not support removing historical records."
218
+ )
219
+
220
+ def remove_records_by_indices(
221
+ self, indices: List[int]
222
+ ) -> List[MemoryRecord]:
223
+ r"""Removing by indices is unsupported for vector database memory."""
224
+ raise NotImplementedError(
225
+ "VectorDBMemory does not support removing records by indices."
226
+ )
227
+
155
228
 
156
229
  class LongtermAgentMemory(AgentMemory):
157
230
  r"""An implementation of the :obj:`AgentMemory` abstract base class for
@@ -236,3 +309,13 @@ class LongtermAgentMemory(AgentMemory):
236
309
  r"""Removes all records from the memory."""
237
310
  self.chat_history_block.clear()
238
311
  self.vector_db_block.clear()
312
+
313
+ def pop_records(self, count: int) -> List[MemoryRecord]:
314
+ r"""Removes recent chat history records while leaving vector memory."""
315
+ return self.chat_history_block.pop_records(count)
316
+
317
+ def remove_records_by_indices(
318
+ self, indices: List[int]
319
+ ) -> List[MemoryRecord]:
320
+ r"""Removes records at specified indices from chat history."""
321
+ return self.chat_history_block.remove_records_by_indices(indices)
camel/memories/base.py CHANGED
@@ -45,6 +45,32 @@ class MemoryBlock(ABC):
45
45
  """
46
46
  self.write_records([record])
47
47
 
48
+ def pop_records(self, count: int) -> List[MemoryRecord]:
49
+ r"""Removes records from the memory and returns the removed records.
50
+
51
+ Args:
52
+ count (int): Number of records to remove.
53
+
54
+ Returns:
55
+ List[MemoryRecord]: The records that were removed from the memory
56
+ in their original order.
57
+ """
58
+ raise NotImplementedError
59
+
60
+ def remove_records_by_indices(
61
+ self, indices: List[int]
62
+ ) -> List[MemoryRecord]:
63
+ r"""Removes records at specified indices from the memory.
64
+
65
+ Args:
66
+ indices (List[int]): List of indices to remove. Indices should be
67
+ valid positions in the current record list.
68
+
69
+ Returns:
70
+ List[MemoryRecord]: The removed records in their original order.
71
+ """
72
+ raise NotImplementedError
73
+
48
74
  @abstractmethod
49
75
  def clear(self) -> None:
50
76
  r"""Clears all messages from the memory."""
@@ -149,6 +175,14 @@ class AgentMemory(MemoryBlock, ABC):
149
175
  """
150
176
  return self.get_context_creator().create_context(self.retrieve())
151
177
 
178
+ def clean_tool_calls(self) -> None:
179
+ r"""Removes tool call messages from memory.
180
+ This is an optional method that can be overridden by subclasses
181
+ to implement cleaning of tool-related messages. By default, it
182
+ does nothing, maintaining backward compatibility.
183
+ """
184
+ pass
185
+
152
186
  def __repr__(self) -> str:
153
187
  r"""Returns a string representation of the AgentMemory.
154
188
 
@@ -11,7 +11,6 @@
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 warnings
15
14
  from typing import List, Optional
16
15
 
17
16
  from camel.memories.base import MemoryBlock
@@ -39,7 +38,7 @@ class ChatHistoryBlock(MemoryBlock):
39
38
  last message is 1.0, and with each step taken backward, the score
40
39
  of the message is multiplied by the `keep_rate`. Higher `keep_rate`
41
40
  leads to high possibility to keep history messages during context
42
- creation.
41
+ creation. (default: :obj:`0.9`)
43
42
  """
44
43
 
45
44
  def __init__(
@@ -70,7 +69,8 @@ class ChatHistoryBlock(MemoryBlock):
70
69
  """
71
70
  record_dicts = self.storage.load()
72
71
  if len(record_dicts) == 0:
73
- warnings.warn("The `ChatHistoryMemory` is empty.")
72
+ # Empty memory is a valid state (e.g., during initialization).
73
+ # Users can check if memory is empty by checking the returned list.
74
74
  return list()
75
75
 
76
76
  if window_size is not None and window_size >= 0:
@@ -81,7 +81,10 @@ class ChatHistoryBlock(MemoryBlock):
81
81
  if (
82
82
  record_dicts
83
83
  and record_dicts[0]['role_at_backend']
84
- in {OpenAIBackendRole.SYSTEM, OpenAIBackendRole.DEVELOPER}
84
+ in {
85
+ OpenAIBackendRole.SYSTEM.value,
86
+ OpenAIBackendRole.DEVELOPER.value,
87
+ }
85
88
  )
86
89
  else 0
87
90
  )
@@ -164,3 +167,118 @@ class ChatHistoryBlock(MemoryBlock):
164
167
  def clear(self) -> None:
165
168
  r"""Clears all chat messages from the memory."""
166
169
  self.storage.clear()
170
+
171
+ def pop_records(self, count: int) -> List[MemoryRecord]:
172
+ r"""Removes the most recent records from the memory.
173
+
174
+ Args:
175
+ count (int): Number of records to remove from the end of the
176
+ conversation history. A value of 0 results in no changes.
177
+
178
+ Returns:
179
+ List[MemoryRecord]: The removed records in chronological order.
180
+ """
181
+ if not isinstance(count, int):
182
+ raise TypeError("`count` must be an integer.")
183
+ if count < 0:
184
+ raise ValueError("`count` must be non-negative.")
185
+ if count == 0:
186
+ return []
187
+
188
+ record_dicts = self.storage.load()
189
+ if not record_dicts:
190
+ return []
191
+
192
+ # Preserve initial system/developer instruction if present.
193
+ protected_prefix = (
194
+ 1
195
+ if (
196
+ record_dicts
197
+ and record_dicts[0]['role_at_backend']
198
+ in {
199
+ OpenAIBackendRole.SYSTEM.value,
200
+ OpenAIBackendRole.DEVELOPER.value,
201
+ }
202
+ )
203
+ else 0
204
+ )
205
+
206
+ removable_count = max(len(record_dicts) - protected_prefix, 0)
207
+ if removable_count == 0:
208
+ return []
209
+
210
+ pop_count = min(count, removable_count)
211
+ split_index = len(record_dicts) - pop_count
212
+
213
+ popped_dicts = record_dicts[split_index:]
214
+ remaining_dicts = record_dicts[:split_index]
215
+
216
+ self.storage.clear()
217
+ if remaining_dicts:
218
+ self.storage.save(remaining_dicts)
219
+
220
+ return [MemoryRecord.from_dict(record) for record in popped_dicts]
221
+
222
+ def remove_records_by_indices(
223
+ self, indices: List[int]
224
+ ) -> List[MemoryRecord]:
225
+ r"""Removes records at specified indices from the memory.
226
+
227
+ Args:
228
+ indices (List[int]): List of indices to remove. Indices are
229
+ positions in the current record list (0-based).
230
+ System/developer messages at index 0 are protected and will
231
+ not be removed.
232
+
233
+ Returns:
234
+ List[MemoryRecord]: The removed records in their original order.
235
+ """
236
+ if not indices:
237
+ return []
238
+
239
+ record_dicts = self.storage.load()
240
+ if not record_dicts:
241
+ return []
242
+
243
+ # Preserve initial system/developer instruction if present.
244
+ protected_prefix = (
245
+ 1
246
+ if (
247
+ record_dicts
248
+ and record_dicts[0]['role_at_backend']
249
+ in {
250
+ OpenAIBackendRole.SYSTEM.value,
251
+ OpenAIBackendRole.DEVELOPER.value,
252
+ }
253
+ )
254
+ else 0
255
+ )
256
+
257
+ # Filter out protected indices and invalid ones
258
+ valid_indices = sorted(
259
+ {
260
+ idx
261
+ for idx in indices
262
+ if idx >= protected_prefix and idx < len(record_dicts)
263
+ }
264
+ )
265
+
266
+ if not valid_indices:
267
+ return []
268
+
269
+ # Extract records to remove (in original order)
270
+ removed_records = [record_dicts[idx] for idx in valid_indices]
271
+
272
+ # Build remaining records by excluding removed indices
273
+ remaining_dicts = [
274
+ record
275
+ for idx, record in enumerate(record_dicts)
276
+ if idx not in valid_indices
277
+ ]
278
+
279
+ # Save back to storage
280
+ self.storage.clear()
281
+ if remaining_dicts:
282
+ self.storage.save(remaining_dicts)
283
+
284
+ return [MemoryRecord.from_dict(record) for record in removed_records]
@@ -89,13 +89,20 @@ class VectorDBBlock(MemoryBlock):
89
89
  records (List[MemoryRecord]): Memory records to be added to the
90
90
  memory.
91
91
  """
92
+ # Filter out records with empty message content
93
+ valid_records = [
94
+ record
95
+ for record in records
96
+ if record.message.content and record.message.content.strip()
97
+ ]
98
+
92
99
  v_records = [
93
100
  VectorRecord(
94
101
  vector=self.embedding.embed(record.message.content),
95
102
  payload=record.to_dict(),
96
103
  id=str(record.uuid),
97
104
  )
98
- for record in records
105
+ for record in valid_records
99
106
  ]
100
107
  self.storage.add(v_records)
101
108