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.
- camel/__init__.py +1 -1
- camel/agents/_types.py +6 -2
- camel/agents/_utils.py +38 -0
- camel/agents/chat_agent.py +4014 -410
- camel/agents/mcp_agent.py +30 -27
- camel/agents/repo_agent.py +2 -1
- camel/benchmarks/browsecomp.py +6 -6
- camel/configs/__init__.py +15 -0
- camel/configs/aihubmix_config.py +88 -0
- camel/configs/amd_config.py +70 -0
- camel/configs/cometapi_config.py +104 -0
- camel/configs/minimax_config.py +93 -0
- camel/configs/nebius_config.py +103 -0
- camel/configs/vllm_config.py +2 -0
- camel/data_collectors/alpaca_collector.py +15 -6
- camel/datagen/self_improving_cot.py +1 -1
- camel/datasets/base_generator.py +39 -10
- camel/environments/__init__.py +12 -0
- camel/environments/rlcards_env.py +860 -0
- camel/environments/single_step.py +28 -3
- camel/environments/tic_tac_toe.py +1 -1
- camel/interpreters/__init__.py +2 -0
- camel/interpreters/docker/Dockerfile +4 -16
- camel/interpreters/docker_interpreter.py +3 -2
- camel/interpreters/e2b_interpreter.py +34 -1
- camel/interpreters/internal_python_interpreter.py +51 -2
- camel/interpreters/microsandbox_interpreter.py +395 -0
- camel/loaders/__init__.py +11 -2
- camel/loaders/base_loader.py +85 -0
- camel/loaders/chunkr_reader.py +9 -0
- camel/loaders/firecrawl_reader.py +4 -4
- camel/logger.py +1 -1
- camel/memories/agent_memories.py +84 -1
- camel/memories/base.py +34 -0
- camel/memories/blocks/chat_history_block.py +122 -4
- camel/memories/blocks/vectordb_block.py +8 -1
- camel/memories/context_creators/score_based.py +29 -237
- camel/memories/records.py +88 -8
- camel/messages/base.py +166 -40
- camel/messages/func_message.py +32 -5
- camel/models/__init__.py +10 -0
- camel/models/aihubmix_model.py +83 -0
- camel/models/aiml_model.py +1 -16
- camel/models/amd_model.py +101 -0
- camel/models/anthropic_model.py +117 -18
- camel/models/aws_bedrock_model.py +2 -33
- camel/models/azure_openai_model.py +205 -91
- camel/models/base_audio_model.py +3 -1
- camel/models/base_model.py +189 -24
- camel/models/cohere_model.py +5 -17
- camel/models/cometapi_model.py +83 -0
- camel/models/crynux_model.py +1 -16
- camel/models/deepseek_model.py +6 -16
- camel/models/fish_audio_model.py +6 -0
- camel/models/gemini_model.py +71 -20
- camel/models/groq_model.py +1 -17
- camel/models/internlm_model.py +1 -16
- camel/models/litellm_model.py +49 -32
- camel/models/lmstudio_model.py +1 -17
- camel/models/minimax_model.py +83 -0
- camel/models/mistral_model.py +1 -16
- camel/models/model_factory.py +27 -1
- camel/models/model_manager.py +24 -6
- camel/models/modelscope_model.py +1 -16
- camel/models/moonshot_model.py +185 -19
- camel/models/nebius_model.py +83 -0
- camel/models/nemotron_model.py +0 -5
- camel/models/netmind_model.py +1 -16
- camel/models/novita_model.py +1 -16
- camel/models/nvidia_model.py +1 -16
- camel/models/ollama_model.py +4 -19
- camel/models/openai_compatible_model.py +171 -46
- camel/models/openai_model.py +205 -77
- camel/models/openrouter_model.py +1 -17
- camel/models/ppio_model.py +1 -16
- camel/models/qianfan_model.py +1 -16
- camel/models/qwen_model.py +1 -16
- camel/models/reka_model.py +1 -16
- camel/models/samba_model.py +34 -47
- camel/models/sglang_model.py +64 -31
- camel/models/siliconflow_model.py +1 -16
- camel/models/stub_model.py +0 -4
- camel/models/togetherai_model.py +1 -16
- camel/models/vllm_model.py +1 -16
- camel/models/volcano_model.py +0 -17
- camel/models/watsonx_model.py +1 -16
- camel/models/yi_model.py +1 -16
- camel/models/zhipuai_model.py +60 -16
- camel/parsers/__init__.py +18 -0
- camel/parsers/mcp_tool_call_parser.py +176 -0
- camel/retrievers/auto_retriever.py +1 -0
- camel/runtimes/configs.py +11 -11
- camel/runtimes/daytona_runtime.py +15 -16
- camel/runtimes/docker_runtime.py +6 -6
- camel/runtimes/remote_http_runtime.py +5 -5
- camel/services/agent_openapi_server.py +380 -0
- camel/societies/__init__.py +2 -0
- camel/societies/role_playing.py +26 -28
- camel/societies/workforce/__init__.py +2 -0
- camel/societies/workforce/events.py +122 -0
- camel/societies/workforce/prompts.py +249 -38
- camel/societies/workforce/role_playing_worker.py +82 -20
- camel/societies/workforce/single_agent_worker.py +634 -34
- camel/societies/workforce/structured_output_handler.py +512 -0
- camel/societies/workforce/task_channel.py +169 -23
- camel/societies/workforce/utils.py +176 -9
- camel/societies/workforce/worker.py +77 -23
- camel/societies/workforce/workflow_memory_manager.py +772 -0
- camel/societies/workforce/workforce.py +3168 -478
- camel/societies/workforce/workforce_callback.py +74 -0
- camel/societies/workforce/workforce_logger.py +203 -175
- camel/societies/workforce/workforce_metrics.py +33 -0
- camel/storages/__init__.py +4 -0
- camel/storages/key_value_storages/json.py +15 -2
- camel/storages/key_value_storages/mem0_cloud.py +48 -47
- camel/storages/object_storages/google_cloud.py +1 -1
- camel/storages/vectordb_storages/__init__.py +6 -0
- camel/storages/vectordb_storages/chroma.py +731 -0
- camel/storages/vectordb_storages/oceanbase.py +13 -13
- camel/storages/vectordb_storages/pgvector.py +349 -0
- camel/storages/vectordb_storages/qdrant.py +3 -3
- camel/storages/vectordb_storages/surreal.py +365 -0
- camel/storages/vectordb_storages/tidb.py +8 -6
- camel/tasks/task.py +244 -27
- camel/toolkits/__init__.py +46 -8
- camel/toolkits/aci_toolkit.py +64 -19
- camel/toolkits/arxiv_toolkit.py +6 -6
- camel/toolkits/base.py +63 -5
- camel/toolkits/code_execution.py +28 -1
- camel/toolkits/context_summarizer_toolkit.py +684 -0
- camel/toolkits/craw4ai_toolkit.py +93 -0
- camel/toolkits/dappier_toolkit.py +10 -6
- camel/toolkits/dingtalk.py +1135 -0
- camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
- camel/toolkits/excel_toolkit.py +901 -67
- camel/toolkits/file_toolkit.py +1402 -0
- camel/toolkits/function_tool.py +30 -6
- camel/toolkits/github_toolkit.py +107 -20
- camel/toolkits/gmail_toolkit.py +1839 -0
- camel/toolkits/google_calendar_toolkit.py +38 -4
- camel/toolkits/google_drive_mcp_toolkit.py +54 -0
- camel/toolkits/human_toolkit.py +34 -10
- camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
- camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
- camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +3749 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package.json +32 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1815 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +590 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
- 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 +130 -0
- camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +26 -0
- camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1032 -0
- camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
- camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
- camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
- camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
- camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
- camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
- camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
- camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
- camel/toolkits/image_generation_toolkit.py +390 -0
- camel/toolkits/jina_reranker_toolkit.py +3 -4
- camel/toolkits/klavis_toolkit.py +5 -1
- camel/toolkits/markitdown_toolkit.py +104 -0
- camel/toolkits/math_toolkit.py +64 -10
- camel/toolkits/mcp_toolkit.py +370 -45
- camel/toolkits/memory_toolkit.py +5 -1
- camel/toolkits/message_agent_toolkit.py +608 -0
- camel/toolkits/message_integration.py +724 -0
- camel/toolkits/minimax_mcp_toolkit.py +195 -0
- camel/toolkits/note_taking_toolkit.py +277 -0
- camel/toolkits/notion_mcp_toolkit.py +224 -0
- camel/toolkits/openbb_toolkit.py +5 -1
- camel/toolkits/origene_mcp_toolkit.py +56 -0
- camel/toolkits/playwright_mcp_toolkit.py +12 -31
- camel/toolkits/pptx_toolkit.py +25 -12
- camel/toolkits/resend_toolkit.py +168 -0
- camel/toolkits/screenshot_toolkit.py +213 -0
- camel/toolkits/search_toolkit.py +437 -142
- camel/toolkits/slack_toolkit.py +104 -50
- camel/toolkits/sympy_toolkit.py +1 -1
- camel/toolkits/task_planning_toolkit.py +3 -3
- camel/toolkits/terminal_toolkit/__init__.py +18 -0
- camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
- camel/toolkits/terminal_toolkit/utils.py +532 -0
- camel/toolkits/thinking_toolkit.py +1 -1
- camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
- camel/toolkits/video_analysis_toolkit.py +106 -26
- camel/toolkits/video_download_toolkit.py +17 -14
- camel/toolkits/web_deploy_toolkit.py +1219 -0
- camel/toolkits/wechat_official_toolkit.py +483 -0
- camel/toolkits/zapier_toolkit.py +5 -1
- camel/types/__init__.py +2 -2
- camel/types/agents/tool_calling_record.py +4 -1
- camel/types/enums.py +316 -40
- camel/types/openai_types.py +2 -2
- camel/types/unified_model_type.py +31 -4
- camel/utils/commons.py +36 -5
- camel/utils/constants.py +3 -0
- camel/utils/context_utils.py +1003 -0
- camel/utils/mcp.py +138 -4
- camel/utils/mcp_client.py +45 -1
- camel/utils/message_summarizer.py +148 -0
- camel/utils/token_counting.py +43 -20
- camel/utils/tool_result.py +44 -0
- {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +296 -85
- {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +219 -146
- camel/loaders/pandas_reader.py +0 -368
- camel/toolkits/dalle_toolkit.py +0 -175
- camel/toolkits/file_write_toolkit.py +0 -444
- camel/toolkits/openai_agent_toolkit.py +0 -135
- camel/toolkits/terminal_toolkit.py +0 -1037
- {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
- {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
|
camel/loaders/chunkr_reader.py
CHANGED
|
@@ -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,
|
|
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,
|
|
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
camel/memories/agent_memories.py
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
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 {
|
|
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
|
|
105
|
+
for record in valid_records
|
|
99
106
|
]
|
|
100
107
|
self.storage.add(v_records)
|
|
101
108
|
|