camel-ai 0.2.27__py3-none-any.whl → 0.2.29__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 +151 -4
- camel/datasets/__init__.py +2 -6
- camel/datasets/base.py +269 -801
- camel/environments/base.py +3 -5
- camel/models/azure_openai_model.py +2 -1
- camel/models/sglang_model.py +3 -2
- camel/models/vllm_model.py +22 -2
- camel/storages/graph_storages/neo4j_graph.py +10 -8
- camel/toolkits/__init__.py +1 -0
- camel/toolkits/mcp_toolkit.py +296 -38
- {camel_ai-0.2.27.dist-info → camel_ai-0.2.29.dist-info}/METADATA +2 -2
- {camel_ai-0.2.27.dist-info → camel_ai-0.2.29.dist-info}/RECORD +15 -15
- {camel_ai-0.2.27.dist-info → camel_ai-0.2.29.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.27.dist-info → camel_ai-0.2.29.dist-info}/licenses/LICENSE +0 -0
camel/__init__.py
CHANGED
camel/agents/chat_agent.py
CHANGED
|
@@ -707,10 +707,13 @@ class ChatAgent(BaseAgent):
|
|
|
707
707
|
f"did not run successfully. Error: {error_info}"
|
|
708
708
|
)
|
|
709
709
|
|
|
710
|
+
sanitized_messages = self._sanitize_messages_for_logging(
|
|
711
|
+
openai_messages
|
|
712
|
+
)
|
|
710
713
|
logger.info(
|
|
711
714
|
f"Model {self.model_backend.model_type}, "
|
|
712
715
|
f"index {self.model_backend.current_model_index}, "
|
|
713
|
-
f"processed these messages: {
|
|
716
|
+
f"processed these messages: {sanitized_messages}"
|
|
714
717
|
)
|
|
715
718
|
|
|
716
719
|
if isinstance(response, ChatCompletion):
|
|
@@ -752,10 +755,13 @@ class ChatAgent(BaseAgent):
|
|
|
752
755
|
f"did not run successfully. Error: {error_info}"
|
|
753
756
|
)
|
|
754
757
|
|
|
758
|
+
sanitized_messages = self._sanitize_messages_for_logging(
|
|
759
|
+
openai_messages
|
|
760
|
+
)
|
|
755
761
|
logger.info(
|
|
756
762
|
f"Model {self.model_backend.model_type}, "
|
|
757
763
|
f"index {self.model_backend.current_model_index}, "
|
|
758
|
-
f"processed these messages: {
|
|
764
|
+
f"processed these messages: {sanitized_messages}"
|
|
759
765
|
)
|
|
760
766
|
|
|
761
767
|
if isinstance(response, ChatCompletion):
|
|
@@ -763,6 +769,134 @@ class ChatAgent(BaseAgent):
|
|
|
763
769
|
else:
|
|
764
770
|
return await self._ahandle_stream_response(response, num_tokens)
|
|
765
771
|
|
|
772
|
+
def _sanitize_messages_for_logging(self, messages):
|
|
773
|
+
r"""Sanitize OpenAI messages for logging by replacing base64 image
|
|
774
|
+
data with a simple message and a link to view the image.
|
|
775
|
+
|
|
776
|
+
Args:
|
|
777
|
+
messages (List[OpenAIMessage]): The OpenAI messages to sanitize.
|
|
778
|
+
|
|
779
|
+
Returns:
|
|
780
|
+
List[OpenAIMessage]: The sanitized OpenAI messages.
|
|
781
|
+
"""
|
|
782
|
+
import hashlib
|
|
783
|
+
import os
|
|
784
|
+
import re
|
|
785
|
+
import tempfile
|
|
786
|
+
|
|
787
|
+
# Create a copy of messages for logging to avoid modifying the
|
|
788
|
+
# original messages
|
|
789
|
+
sanitized_messages = []
|
|
790
|
+
for msg in messages:
|
|
791
|
+
if isinstance(msg, dict):
|
|
792
|
+
sanitized_msg = msg.copy()
|
|
793
|
+
# Check if content is a list (multimodal content with images)
|
|
794
|
+
if isinstance(sanitized_msg.get('content'), list):
|
|
795
|
+
content_list = []
|
|
796
|
+
for item in sanitized_msg['content']:
|
|
797
|
+
if (
|
|
798
|
+
isinstance(item, dict)
|
|
799
|
+
and item.get('type') == 'image_url'
|
|
800
|
+
):
|
|
801
|
+
# Handle image URL
|
|
802
|
+
image_url = item.get('image_url', {}).get(
|
|
803
|
+
'url', ''
|
|
804
|
+
)
|
|
805
|
+
if image_url and image_url.startswith(
|
|
806
|
+
'data:image'
|
|
807
|
+
):
|
|
808
|
+
# Extract image data and format
|
|
809
|
+
match = re.match(
|
|
810
|
+
r'data:image/([^;]+);base64,(.+)',
|
|
811
|
+
image_url,
|
|
812
|
+
)
|
|
813
|
+
if match:
|
|
814
|
+
img_format, base64_data = match.groups()
|
|
815
|
+
|
|
816
|
+
# Create a hash of the image data to use
|
|
817
|
+
# as filename
|
|
818
|
+
img_hash = hashlib.md5(
|
|
819
|
+
base64_data[:100].encode()
|
|
820
|
+
).hexdigest()[:10]
|
|
821
|
+
img_filename = (
|
|
822
|
+
f"image_{img_hash}.{img_format}"
|
|
823
|
+
)
|
|
824
|
+
|
|
825
|
+
# Save image to temp directory for viewing
|
|
826
|
+
try:
|
|
827
|
+
import base64
|
|
828
|
+
|
|
829
|
+
temp_dir = tempfile.gettempdir()
|
|
830
|
+
img_path = os.path.join(
|
|
831
|
+
temp_dir, img_filename
|
|
832
|
+
)
|
|
833
|
+
|
|
834
|
+
# Only save if file doesn't exist
|
|
835
|
+
if not os.path.exists(img_path):
|
|
836
|
+
with open(img_path, 'wb') as f:
|
|
837
|
+
f.write(
|
|
838
|
+
base64.b64decode(
|
|
839
|
+
base64_data
|
|
840
|
+
)
|
|
841
|
+
)
|
|
842
|
+
|
|
843
|
+
# Create a file:// URL that can be
|
|
844
|
+
# opened
|
|
845
|
+
file_url = f"file://{img_path}"
|
|
846
|
+
|
|
847
|
+
content_list.append(
|
|
848
|
+
{
|
|
849
|
+
'type': 'image_url',
|
|
850
|
+
'image_url': {
|
|
851
|
+
'url': f'{file_url}',
|
|
852
|
+
'detail': item.get(
|
|
853
|
+
'image_url', {}
|
|
854
|
+
).get('detail', 'auto'),
|
|
855
|
+
},
|
|
856
|
+
}
|
|
857
|
+
)
|
|
858
|
+
except Exception as e:
|
|
859
|
+
# If saving fails, fall back to simple
|
|
860
|
+
# message
|
|
861
|
+
content_list.append(
|
|
862
|
+
{
|
|
863
|
+
'type': 'image_url',
|
|
864
|
+
'image_url': {
|
|
865
|
+
'url': '[base64 '
|
|
866
|
+
+ 'image - error saving: '
|
|
867
|
+
+ str(e)
|
|
868
|
+
+ ']',
|
|
869
|
+
'detail': item.get(
|
|
870
|
+
'image_url', {}
|
|
871
|
+
).get('detail', 'auto'),
|
|
872
|
+
},
|
|
873
|
+
}
|
|
874
|
+
)
|
|
875
|
+
else:
|
|
876
|
+
# If regex fails, fall back to simple
|
|
877
|
+
# message
|
|
878
|
+
content_list.append(
|
|
879
|
+
{
|
|
880
|
+
'type': 'image_url',
|
|
881
|
+
'image_url': {
|
|
882
|
+
'url': '[base64 '
|
|
883
|
+
+ 'image - invalid format]',
|
|
884
|
+
'detail': item.get(
|
|
885
|
+
'image_url', {}
|
|
886
|
+
).get('detail', 'auto'),
|
|
887
|
+
},
|
|
888
|
+
}
|
|
889
|
+
)
|
|
890
|
+
else:
|
|
891
|
+
content_list.append(item)
|
|
892
|
+
else:
|
|
893
|
+
content_list.append(item)
|
|
894
|
+
sanitized_msg['content'] = content_list
|
|
895
|
+
sanitized_messages.append(sanitized_msg)
|
|
896
|
+
else:
|
|
897
|
+
sanitized_messages.append(msg)
|
|
898
|
+
return sanitized_messages
|
|
899
|
+
|
|
766
900
|
def _step_get_info(
|
|
767
901
|
self,
|
|
768
902
|
output_messages: List[BaseMessage],
|
|
@@ -1043,7 +1177,13 @@ class ChatAgent(BaseAgent):
|
|
|
1043
1177
|
args = tool_call_request.args
|
|
1044
1178
|
tool_call_id = tool_call_request.tool_call_id
|
|
1045
1179
|
tool = self._internal_tools[func_name]
|
|
1046
|
-
|
|
1180
|
+
try:
|
|
1181
|
+
result = tool(**args)
|
|
1182
|
+
except Exception as e:
|
|
1183
|
+
# Capture the error message to prevent framework crash
|
|
1184
|
+
error_msg = f"Error executing tool '{func_name}': {e!s}"
|
|
1185
|
+
result = {"error": error_msg}
|
|
1186
|
+
logging.warning(error_msg)
|
|
1047
1187
|
|
|
1048
1188
|
return self._record_tool_calling(func_name, args, result, tool_call_id)
|
|
1049
1189
|
|
|
@@ -1055,7 +1195,14 @@ class ChatAgent(BaseAgent):
|
|
|
1055
1195
|
args = tool_call_request.args
|
|
1056
1196
|
tool_call_id = tool_call_request.tool_call_id
|
|
1057
1197
|
tool = self._internal_tools[func_name]
|
|
1058
|
-
|
|
1198
|
+
try:
|
|
1199
|
+
result = await tool.async_call(**args)
|
|
1200
|
+
except Exception as e:
|
|
1201
|
+
# Capture the error message to prevent framework crash
|
|
1202
|
+
error_msg = f"Error executing async tool '{func_name}': {e!s}"
|
|
1203
|
+
result = {"error": error_msg}
|
|
1204
|
+
logging.warning(error_msg)
|
|
1205
|
+
|
|
1059
1206
|
return self._record_tool_calling(func_name, args, result, tool_call_id)
|
|
1060
1207
|
|
|
1061
1208
|
def _record_tool_calling(
|
camel/datasets/__init__.py
CHANGED
|
@@ -12,17 +12,13 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
from .base import (
|
|
15
|
-
BaseDataset,
|
|
16
15
|
DataPoint,
|
|
17
16
|
GenerativeDataset,
|
|
18
|
-
|
|
19
|
-
SyntheticDataset,
|
|
17
|
+
StaticDataset,
|
|
20
18
|
)
|
|
21
19
|
|
|
22
20
|
__all__ = [
|
|
23
21
|
"DataPoint",
|
|
24
|
-
"
|
|
25
|
-
"SeedDataset",
|
|
22
|
+
"StaticDataset",
|
|
26
23
|
"GenerativeDataset",
|
|
27
|
-
"SyntheticDataset",
|
|
28
24
|
]
|