swarms 7.7.8__py3-none-any.whl → 7.7.9__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.
- swarms/__init__.py +0 -1
- swarms/agents/cort_agent.py +206 -0
- swarms/agents/react_agent.py +173 -0
- swarms/communication/base_communication.py +290 -0
- swarms/communication/duckdb_wrap.py +369 -72
- swarms/communication/pulsar_struct.py +691 -0
- swarms/communication/redis_wrap.py +1362 -0
- swarms/communication/sqlite_wrap.py +547 -44
- swarms/prompts/safety_prompt.py +50 -0
- swarms/structs/agent.py +10 -5
- swarms/structs/conversation.py +228 -38
- swarms/structs/council_judge.py +456 -0
- swarms/structs/deep_research_swarm.py +19 -22
- swarms/structs/malt.py +30 -28
- swarms/structs/multi_model_gpu_manager.py +1 -1
- swarms/structs/output_types.py +1 -1
- swarms/structs/swarm_router.py +2 -2
- swarms/tools/mcp_client.py +1 -1
- swarms/tools/py_func_to_openai_func_str.py +2 -2
- swarms/utils/history_output_formatter.py +5 -5
- swarms/utils/try_except_wrapper.py +2 -2
- swarms/utils/xml_utils.py +42 -0
- {swarms-7.7.8.dist-info → swarms-7.7.9.dist-info}/METADATA +4 -3
- {swarms-7.7.8.dist-info → swarms-7.7.9.dist-info}/RECORD +27 -21
- {swarms-7.7.8.dist-info → swarms-7.7.9.dist-info}/WHEEL +1 -1
- swarms/client/__init__.py +0 -15
- swarms/client/main.py +0 -407
- {swarms-7.7.8.dist-info → swarms-7.7.9.dist-info}/LICENSE +0 -0
- {swarms-7.7.8.dist-info → swarms-7.7.9.dist-info}/entry_points.txt +0 -0
@@ -1,15 +1,19 @@
|
|
1
1
|
import sqlite3
|
2
2
|
import json
|
3
3
|
import datetime
|
4
|
-
from typing import List, Optional, Union, Dict
|
4
|
+
from typing import List, Optional, Union, Dict, Any
|
5
5
|
from pathlib import Path
|
6
6
|
import threading
|
7
7
|
from contextlib import contextmanager
|
8
8
|
import logging
|
9
|
-
from dataclasses import dataclass
|
10
|
-
from enum import Enum
|
11
9
|
import uuid
|
12
10
|
import yaml
|
11
|
+
from swarms.communication.base_communication import (
|
12
|
+
BaseCommunication,
|
13
|
+
Message,
|
14
|
+
MessageType,
|
15
|
+
)
|
16
|
+
from typing import Callable
|
13
17
|
|
14
18
|
try:
|
15
19
|
from loguru import logger
|
@@ -19,32 +23,7 @@ except ImportError:
|
|
19
23
|
LOGURU_AVAILABLE = False
|
20
24
|
|
21
25
|
|
22
|
-
class
|
23
|
-
"""Enum for different types of messages in the conversation."""
|
24
|
-
|
25
|
-
SYSTEM = "system"
|
26
|
-
USER = "user"
|
27
|
-
ASSISTANT = "assistant"
|
28
|
-
FUNCTION = "function"
|
29
|
-
TOOL = "tool"
|
30
|
-
|
31
|
-
|
32
|
-
@dataclass
|
33
|
-
class Message:
|
34
|
-
"""Data class representing a message in the conversation."""
|
35
|
-
|
36
|
-
role: str
|
37
|
-
content: Union[str, dict, list]
|
38
|
-
timestamp: Optional[str] = None
|
39
|
-
message_type: Optional[MessageType] = None
|
40
|
-
metadata: Optional[Dict] = None
|
41
|
-
token_count: Optional[int] = None
|
42
|
-
|
43
|
-
class Config:
|
44
|
-
arbitrary_types_allowed = True
|
45
|
-
|
46
|
-
|
47
|
-
class SQLiteConversation:
|
26
|
+
class SQLiteConversation(BaseCommunication):
|
48
27
|
"""
|
49
28
|
A production-grade SQLite wrapper class for managing conversation history.
|
50
29
|
This class provides persistent storage for conversations with various features
|
@@ -63,7 +42,21 @@ class SQLiteConversation:
|
|
63
42
|
|
64
43
|
def __init__(
|
65
44
|
self,
|
66
|
-
|
45
|
+
system_prompt: Optional[str] = None,
|
46
|
+
time_enabled: bool = False,
|
47
|
+
autosave: bool = False,
|
48
|
+
save_filepath: str = None,
|
49
|
+
tokenizer: Any = None,
|
50
|
+
context_length: int = 8192,
|
51
|
+
rules: str = None,
|
52
|
+
custom_rules_prompt: str = None,
|
53
|
+
user: str = "User:",
|
54
|
+
auto_save: bool = True,
|
55
|
+
save_as_yaml: bool = True,
|
56
|
+
save_as_json_bool: bool = False,
|
57
|
+
token_count: bool = True,
|
58
|
+
cache_enabled: bool = True,
|
59
|
+
db_path: Union[str, Path] = None,
|
67
60
|
table_name: str = "conversations",
|
68
61
|
enable_timestamps: bool = True,
|
69
62
|
enable_logging: bool = True,
|
@@ -72,19 +65,31 @@ class SQLiteConversation:
|
|
72
65
|
connection_timeout: float = 5.0,
|
73
66
|
**kwargs,
|
74
67
|
):
|
75
|
-
|
76
|
-
|
68
|
+
super().__init__(
|
69
|
+
system_prompt=system_prompt,
|
70
|
+
time_enabled=time_enabled,
|
71
|
+
autosave=autosave,
|
72
|
+
save_filepath=save_filepath,
|
73
|
+
tokenizer=tokenizer,
|
74
|
+
context_length=context_length,
|
75
|
+
rules=rules,
|
76
|
+
custom_rules_prompt=custom_rules_prompt,
|
77
|
+
user=user,
|
78
|
+
auto_save=auto_save,
|
79
|
+
save_as_yaml=save_as_yaml,
|
80
|
+
save_as_json_bool=save_as_json_bool,
|
81
|
+
token_count=token_count,
|
82
|
+
cache_enabled=cache_enabled,
|
83
|
+
)
|
77
84
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
enable_timestamps (bool): Whether to track message timestamps
|
82
|
-
enable_logging (bool): Whether to enable logging
|
83
|
-
use_loguru (bool): Whether to use loguru for logging
|
84
|
-
max_retries (int): Maximum number of retries for database operations
|
85
|
-
connection_timeout (float): Timeout for database connections
|
86
|
-
"""
|
85
|
+
# Calculate default db_path if not provided
|
86
|
+
if db_path is None:
|
87
|
+
db_path = self.get_default_db_path("conversations.sqlite")
|
87
88
|
self.db_path = Path(db_path)
|
89
|
+
|
90
|
+
# Ensure parent directory exists
|
91
|
+
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
92
|
+
|
88
93
|
self.table_name = table_name
|
89
94
|
self.enable_timestamps = enable_timestamps
|
90
95
|
self.enable_logging = enable_logging
|
@@ -92,9 +97,7 @@ class SQLiteConversation:
|
|
92
97
|
self.max_retries = max_retries
|
93
98
|
self.connection_timeout = connection_timeout
|
94
99
|
self._lock = threading.Lock()
|
95
|
-
self.
|
96
|
-
self._generate_conversation_id()
|
97
|
-
)
|
100
|
+
self.tokenizer = tokenizer
|
98
101
|
|
99
102
|
# Setup logging
|
100
103
|
if self.enable_logging:
|
@@ -112,6 +115,7 @@ class SQLiteConversation:
|
|
112
115
|
|
113
116
|
# Initialize database
|
114
117
|
self._init_db()
|
118
|
+
self.start_new_conversation()
|
115
119
|
|
116
120
|
def _generate_conversation_id(self) -> str:
|
117
121
|
"""Generate a unique conversation ID using UUID and timestamp."""
|
@@ -811,3 +815,502 @@ class SQLiteConversation:
|
|
811
815
|
"total_tokens": row["total_tokens"],
|
812
816
|
"roles": self.count_messages_by_role(),
|
813
817
|
}
|
818
|
+
|
819
|
+
def delete(self, index: str):
|
820
|
+
"""Delete a message from the conversation history."""
|
821
|
+
with self._get_connection() as conn:
|
822
|
+
cursor = conn.cursor()
|
823
|
+
cursor.execute(
|
824
|
+
f"DELETE FROM {self.table_name} WHERE id = ? AND conversation_id = ?",
|
825
|
+
(index, self.current_conversation_id),
|
826
|
+
)
|
827
|
+
conn.commit()
|
828
|
+
|
829
|
+
def update(
|
830
|
+
self, index: str, role: str, content: Union[str, dict]
|
831
|
+
):
|
832
|
+
"""Update a message in the conversation history."""
|
833
|
+
if isinstance(content, (dict, list)):
|
834
|
+
content = json.dumps(content)
|
835
|
+
|
836
|
+
with self._get_connection() as conn:
|
837
|
+
cursor = conn.cursor()
|
838
|
+
cursor.execute(
|
839
|
+
f"""
|
840
|
+
UPDATE {self.table_name}
|
841
|
+
SET role = ?, content = ?
|
842
|
+
WHERE id = ? AND conversation_id = ?
|
843
|
+
""",
|
844
|
+
(role, content, index, self.current_conversation_id),
|
845
|
+
)
|
846
|
+
conn.commit()
|
847
|
+
|
848
|
+
def query(self, index: str) -> Dict:
|
849
|
+
"""Query a message in the conversation history."""
|
850
|
+
with self._get_connection() as conn:
|
851
|
+
cursor = conn.cursor()
|
852
|
+
cursor.execute(
|
853
|
+
f"""
|
854
|
+
SELECT * FROM {self.table_name}
|
855
|
+
WHERE id = ? AND conversation_id = ?
|
856
|
+
""",
|
857
|
+
(index, self.current_conversation_id),
|
858
|
+
)
|
859
|
+
row = cursor.fetchone()
|
860
|
+
|
861
|
+
if not row:
|
862
|
+
return {}
|
863
|
+
|
864
|
+
content = row["content"]
|
865
|
+
try:
|
866
|
+
content = json.loads(content)
|
867
|
+
except json.JSONDecodeError:
|
868
|
+
pass
|
869
|
+
|
870
|
+
return {
|
871
|
+
"role": row["role"],
|
872
|
+
"content": content,
|
873
|
+
"timestamp": row["timestamp"],
|
874
|
+
"message_type": row["message_type"],
|
875
|
+
"metadata": (
|
876
|
+
json.loads(row["metadata"])
|
877
|
+
if row["metadata"]
|
878
|
+
else None
|
879
|
+
),
|
880
|
+
"token_count": row["token_count"],
|
881
|
+
}
|
882
|
+
|
883
|
+
def search(self, keyword: str) -> List[Dict]:
|
884
|
+
"""Search for messages containing a keyword."""
|
885
|
+
return self.search_messages(keyword)
|
886
|
+
|
887
|
+
def display_conversation(self, detailed: bool = False):
|
888
|
+
"""Display the conversation history."""
|
889
|
+
print(self.get_str())
|
890
|
+
|
891
|
+
def export_conversation(self, filename: str):
|
892
|
+
"""Export the conversation history to a file."""
|
893
|
+
self.save_as_json(filename)
|
894
|
+
|
895
|
+
def import_conversation(self, filename: str):
|
896
|
+
"""Import a conversation history from a file."""
|
897
|
+
self.load_from_json(filename)
|
898
|
+
|
899
|
+
def return_history_as_string(self) -> str:
|
900
|
+
"""Return the conversation history as a string."""
|
901
|
+
return self.get_str()
|
902
|
+
|
903
|
+
def clear(self):
|
904
|
+
"""Clear the conversation history."""
|
905
|
+
with self._get_connection() as conn:
|
906
|
+
cursor = conn.cursor()
|
907
|
+
cursor.execute(
|
908
|
+
f"DELETE FROM {self.table_name} WHERE conversation_id = ?",
|
909
|
+
(self.current_conversation_id,),
|
910
|
+
)
|
911
|
+
conn.commit()
|
912
|
+
|
913
|
+
def get_conversation_timeline_dict(self) -> Dict[str, List[Dict]]:
|
914
|
+
"""Get the conversation organized by timestamps."""
|
915
|
+
with self._get_connection() as conn:
|
916
|
+
cursor = conn.cursor()
|
917
|
+
cursor.execute(
|
918
|
+
f"""
|
919
|
+
SELECT
|
920
|
+
DATE(timestamp) as date,
|
921
|
+
role,
|
922
|
+
content,
|
923
|
+
timestamp,
|
924
|
+
message_type,
|
925
|
+
metadata,
|
926
|
+
token_count
|
927
|
+
FROM {self.table_name}
|
928
|
+
WHERE conversation_id = ?
|
929
|
+
ORDER BY timestamp ASC
|
930
|
+
""",
|
931
|
+
(self.current_conversation_id,),
|
932
|
+
)
|
933
|
+
|
934
|
+
timeline_dict = {}
|
935
|
+
for row in cursor.fetchall():
|
936
|
+
date = row["date"]
|
937
|
+
content = row["content"]
|
938
|
+
try:
|
939
|
+
content = json.loads(content)
|
940
|
+
except json.JSONDecodeError:
|
941
|
+
pass
|
942
|
+
|
943
|
+
message = {
|
944
|
+
"role": row["role"],
|
945
|
+
"content": content,
|
946
|
+
"timestamp": row["timestamp"],
|
947
|
+
"message_type": row["message_type"],
|
948
|
+
"metadata": (
|
949
|
+
json.loads(row["metadata"])
|
950
|
+
if row["metadata"]
|
951
|
+
else None
|
952
|
+
),
|
953
|
+
"token_count": row["token_count"],
|
954
|
+
}
|
955
|
+
|
956
|
+
if date not in timeline_dict:
|
957
|
+
timeline_dict[date] = []
|
958
|
+
timeline_dict[date].append(message)
|
959
|
+
|
960
|
+
return timeline_dict
|
961
|
+
|
962
|
+
def truncate_memory_with_tokenizer(self):
|
963
|
+
"""Truncate the conversation history based on token count."""
|
964
|
+
if not self.tokenizer:
|
965
|
+
return
|
966
|
+
|
967
|
+
with self._get_connection() as conn:
|
968
|
+
cursor = conn.cursor()
|
969
|
+
cursor.execute(
|
970
|
+
f"""
|
971
|
+
SELECT id, content, token_count
|
972
|
+
FROM {self.table_name}
|
973
|
+
WHERE conversation_id = ?
|
974
|
+
ORDER BY id ASC
|
975
|
+
""",
|
976
|
+
(self.current_conversation_id,),
|
977
|
+
)
|
978
|
+
|
979
|
+
total_tokens = 0
|
980
|
+
ids_to_keep = []
|
981
|
+
|
982
|
+
for row in cursor.fetchall():
|
983
|
+
token_count = row[
|
984
|
+
"token_count"
|
985
|
+
] or self.tokenizer.count_tokens(row["content"])
|
986
|
+
if total_tokens + token_count <= self.context_length:
|
987
|
+
total_tokens += token_count
|
988
|
+
ids_to_keep.append(row["id"])
|
989
|
+
else:
|
990
|
+
break
|
991
|
+
|
992
|
+
if ids_to_keep:
|
993
|
+
ids_str = ",".join(map(str, ids_to_keep))
|
994
|
+
cursor.execute(
|
995
|
+
f"""
|
996
|
+
DELETE FROM {self.table_name}
|
997
|
+
WHERE conversation_id = ?
|
998
|
+
AND id NOT IN ({ids_str})
|
999
|
+
""",
|
1000
|
+
(self.current_conversation_id,),
|
1001
|
+
)
|
1002
|
+
conn.commit()
|
1003
|
+
|
1004
|
+
def get_conversation_metadata_dict(self) -> Dict:
|
1005
|
+
"""Get detailed metadata about the conversation."""
|
1006
|
+
with self._get_connection() as conn:
|
1007
|
+
cursor = conn.cursor()
|
1008
|
+
# Get basic statistics
|
1009
|
+
stats = self.get_statistics()
|
1010
|
+
|
1011
|
+
# Get message type distribution
|
1012
|
+
cursor.execute(
|
1013
|
+
f"""
|
1014
|
+
SELECT message_type, COUNT(*) as count
|
1015
|
+
FROM {self.table_name}
|
1016
|
+
WHERE conversation_id = ?
|
1017
|
+
GROUP BY message_type
|
1018
|
+
""",
|
1019
|
+
(self.current_conversation_id,),
|
1020
|
+
)
|
1021
|
+
type_dist = cursor.fetchall()
|
1022
|
+
|
1023
|
+
# Get average tokens per message
|
1024
|
+
cursor.execute(
|
1025
|
+
f"""
|
1026
|
+
SELECT AVG(token_count) as avg_tokens
|
1027
|
+
FROM {self.table_name}
|
1028
|
+
WHERE conversation_id = ? AND token_count IS NOT NULL
|
1029
|
+
""",
|
1030
|
+
(self.current_conversation_id,),
|
1031
|
+
)
|
1032
|
+
avg_tokens = cursor.fetchone()
|
1033
|
+
|
1034
|
+
# Get message frequency by hour
|
1035
|
+
cursor.execute(
|
1036
|
+
f"""
|
1037
|
+
SELECT
|
1038
|
+
strftime('%H', timestamp) as hour,
|
1039
|
+
COUNT(*) as count
|
1040
|
+
FROM {self.table_name}
|
1041
|
+
WHERE conversation_id = ?
|
1042
|
+
GROUP BY hour
|
1043
|
+
ORDER BY hour
|
1044
|
+
""",
|
1045
|
+
(self.current_conversation_id,),
|
1046
|
+
)
|
1047
|
+
hourly_freq = cursor.fetchall()
|
1048
|
+
|
1049
|
+
return {
|
1050
|
+
"conversation_id": self.current_conversation_id,
|
1051
|
+
"basic_stats": stats,
|
1052
|
+
"message_type_distribution": {
|
1053
|
+
row["message_type"]: row["count"]
|
1054
|
+
for row in type_dist
|
1055
|
+
if row["message_type"]
|
1056
|
+
},
|
1057
|
+
"average_tokens_per_message": (
|
1058
|
+
avg_tokens["avg_tokens"]
|
1059
|
+
if avg_tokens["avg_tokens"] is not None
|
1060
|
+
else 0
|
1061
|
+
),
|
1062
|
+
"hourly_message_frequency": {
|
1063
|
+
row["hour"]: row["count"] for row in hourly_freq
|
1064
|
+
},
|
1065
|
+
"role_distribution": self.count_messages_by_role(),
|
1066
|
+
}
|
1067
|
+
|
1068
|
+
def get_conversation_by_role_dict(self) -> Dict[str, List[Dict]]:
|
1069
|
+
"""Get the conversation organized by roles."""
|
1070
|
+
with self._get_connection() as conn:
|
1071
|
+
cursor = conn.cursor()
|
1072
|
+
cursor.execute(
|
1073
|
+
f"""
|
1074
|
+
SELECT role, content, timestamp, message_type, metadata, token_count
|
1075
|
+
FROM {self.table_name}
|
1076
|
+
WHERE conversation_id = ?
|
1077
|
+
ORDER BY id ASC
|
1078
|
+
""",
|
1079
|
+
(self.current_conversation_id,),
|
1080
|
+
)
|
1081
|
+
|
1082
|
+
role_dict = {}
|
1083
|
+
for row in cursor.fetchall():
|
1084
|
+
role = row["role"]
|
1085
|
+
content = row["content"]
|
1086
|
+
try:
|
1087
|
+
content = json.loads(content)
|
1088
|
+
except json.JSONDecodeError:
|
1089
|
+
pass
|
1090
|
+
|
1091
|
+
message = {
|
1092
|
+
"content": content,
|
1093
|
+
"timestamp": row["timestamp"],
|
1094
|
+
"message_type": row["message_type"],
|
1095
|
+
"metadata": (
|
1096
|
+
json.loads(row["metadata"])
|
1097
|
+
if row["metadata"]
|
1098
|
+
else None
|
1099
|
+
),
|
1100
|
+
"token_count": row["token_count"],
|
1101
|
+
}
|
1102
|
+
|
1103
|
+
if role not in role_dict:
|
1104
|
+
role_dict[role] = []
|
1105
|
+
role_dict[role].append(message)
|
1106
|
+
|
1107
|
+
return role_dict
|
1108
|
+
|
1109
|
+
def get_conversation_as_dict(self) -> Dict:
|
1110
|
+
"""Get the entire conversation as a dictionary with messages and metadata."""
|
1111
|
+
messages = self.get_messages()
|
1112
|
+
stats = self.get_statistics()
|
1113
|
+
|
1114
|
+
return {
|
1115
|
+
"conversation_id": self.current_conversation_id,
|
1116
|
+
"messages": messages,
|
1117
|
+
"metadata": {
|
1118
|
+
"total_messages": stats["total_messages"],
|
1119
|
+
"unique_roles": stats["unique_roles"],
|
1120
|
+
"total_tokens": stats["total_tokens"],
|
1121
|
+
"first_message": stats["first_message"],
|
1122
|
+
"last_message": stats["last_message"],
|
1123
|
+
"roles": self.count_messages_by_role(),
|
1124
|
+
},
|
1125
|
+
}
|
1126
|
+
|
1127
|
+
def get_visible_messages(
|
1128
|
+
self, agent: Callable, turn: int
|
1129
|
+
) -> List[Dict]:
|
1130
|
+
"""
|
1131
|
+
Get the visible messages for a given agent and turn.
|
1132
|
+
|
1133
|
+
Args:
|
1134
|
+
agent (Agent): The agent.
|
1135
|
+
turn (int): The turn number.
|
1136
|
+
|
1137
|
+
Returns:
|
1138
|
+
List[Dict]: The list of visible messages.
|
1139
|
+
"""
|
1140
|
+
with self._get_connection() as conn:
|
1141
|
+
cursor = conn.cursor()
|
1142
|
+
cursor.execute(
|
1143
|
+
f"""
|
1144
|
+
SELECT * FROM {self.table_name}
|
1145
|
+
WHERE conversation_id = ?
|
1146
|
+
AND json_extract(metadata, '$.turn') < ?
|
1147
|
+
ORDER BY id ASC
|
1148
|
+
""",
|
1149
|
+
(self.current_conversation_id, turn),
|
1150
|
+
)
|
1151
|
+
|
1152
|
+
visible_messages = []
|
1153
|
+
for row in cursor.fetchall():
|
1154
|
+
metadata = (
|
1155
|
+
json.loads(row["metadata"])
|
1156
|
+
if row["metadata"]
|
1157
|
+
else {}
|
1158
|
+
)
|
1159
|
+
visible_to = metadata.get("visible_to", "all")
|
1160
|
+
|
1161
|
+
if visible_to == "all" or (
|
1162
|
+
agent and agent.agent_name in visible_to
|
1163
|
+
):
|
1164
|
+
content = row["content"]
|
1165
|
+
try:
|
1166
|
+
content = json.loads(content)
|
1167
|
+
except json.JSONDecodeError:
|
1168
|
+
pass
|
1169
|
+
|
1170
|
+
message = {
|
1171
|
+
"role": row["role"],
|
1172
|
+
"content": content,
|
1173
|
+
"visible_to": visible_to,
|
1174
|
+
"turn": metadata.get("turn"),
|
1175
|
+
}
|
1176
|
+
visible_messages.append(message)
|
1177
|
+
|
1178
|
+
return visible_messages
|
1179
|
+
|
1180
|
+
def return_messages_as_list(self) -> List[str]:
|
1181
|
+
"""Return the conversation messages as a list of formatted strings.
|
1182
|
+
|
1183
|
+
Returns:
|
1184
|
+
list: List of messages formatted as 'role: content'.
|
1185
|
+
"""
|
1186
|
+
with self._get_connection() as conn:
|
1187
|
+
cursor = conn.cursor()
|
1188
|
+
cursor.execute(
|
1189
|
+
f"""
|
1190
|
+
SELECT role, content FROM {self.table_name}
|
1191
|
+
WHERE conversation_id = ?
|
1192
|
+
ORDER BY id ASC
|
1193
|
+
""",
|
1194
|
+
(self.current_conversation_id,),
|
1195
|
+
)
|
1196
|
+
|
1197
|
+
return [
|
1198
|
+
f"{row['role']}: {json.loads(row['content']) if isinstance(row['content'], str) and row['content'].startswith('{') else row['content']}"
|
1199
|
+
for row in cursor.fetchall()
|
1200
|
+
]
|
1201
|
+
|
1202
|
+
def return_messages_as_dictionary(self) -> List[Dict]:
|
1203
|
+
"""Return the conversation messages as a list of dictionaries.
|
1204
|
+
|
1205
|
+
Returns:
|
1206
|
+
list: List of dictionaries containing role and content of each message.
|
1207
|
+
"""
|
1208
|
+
with self._get_connection() as conn:
|
1209
|
+
cursor = conn.cursor()
|
1210
|
+
cursor.execute(
|
1211
|
+
f"""
|
1212
|
+
SELECT role, content FROM {self.table_name}
|
1213
|
+
WHERE conversation_id = ?
|
1214
|
+
ORDER BY id ASC
|
1215
|
+
""",
|
1216
|
+
(self.current_conversation_id,),
|
1217
|
+
)
|
1218
|
+
|
1219
|
+
messages = []
|
1220
|
+
for row in cursor.fetchall():
|
1221
|
+
content = row["content"]
|
1222
|
+
try:
|
1223
|
+
content = json.loads(content)
|
1224
|
+
except json.JSONDecodeError:
|
1225
|
+
pass
|
1226
|
+
|
1227
|
+
messages.append(
|
1228
|
+
{
|
1229
|
+
"role": row["role"],
|
1230
|
+
"content": content,
|
1231
|
+
}
|
1232
|
+
)
|
1233
|
+
return messages
|
1234
|
+
|
1235
|
+
def add_tool_output_to_agent(self, role: str, tool_output: dict):
|
1236
|
+
"""Add a tool output to the conversation history.
|
1237
|
+
|
1238
|
+
Args:
|
1239
|
+
role (str): The role of the tool.
|
1240
|
+
tool_output (dict): The output from the tool to be added.
|
1241
|
+
"""
|
1242
|
+
self.add(role, tool_output, message_type=MessageType.TOOL)
|
1243
|
+
|
1244
|
+
def get_final_message(self) -> str:
|
1245
|
+
"""Return the final message from the conversation history.
|
1246
|
+
|
1247
|
+
Returns:
|
1248
|
+
str: The final message formatted as 'role: content'.
|
1249
|
+
"""
|
1250
|
+
last_message = self.get_last_message()
|
1251
|
+
if not last_message:
|
1252
|
+
return ""
|
1253
|
+
return f"{last_message['role']}: {last_message['content']}"
|
1254
|
+
|
1255
|
+
def get_final_message_content(self) -> Union[str, dict]:
|
1256
|
+
"""Return the content of the final message from the conversation history.
|
1257
|
+
|
1258
|
+
Returns:
|
1259
|
+
Union[str, dict]: The content of the final message.
|
1260
|
+
"""
|
1261
|
+
last_message = self.get_last_message()
|
1262
|
+
if not last_message:
|
1263
|
+
return ""
|
1264
|
+
return last_message["content"]
|
1265
|
+
|
1266
|
+
def return_all_except_first(self) -> List[Dict]:
|
1267
|
+
"""Return all messages except the first one.
|
1268
|
+
|
1269
|
+
Returns:
|
1270
|
+
list: List of messages except the first one.
|
1271
|
+
"""
|
1272
|
+
with self._get_connection() as conn:
|
1273
|
+
cursor = conn.cursor()
|
1274
|
+
cursor.execute(
|
1275
|
+
f"""
|
1276
|
+
SELECT role, content, timestamp, message_type, metadata, token_count
|
1277
|
+
FROM {self.table_name}
|
1278
|
+
WHERE conversation_id = ?
|
1279
|
+
ORDER BY id ASC
|
1280
|
+
LIMIT -1 OFFSET 2
|
1281
|
+
""",
|
1282
|
+
(self.current_conversation_id,),
|
1283
|
+
)
|
1284
|
+
|
1285
|
+
messages = []
|
1286
|
+
for row in cursor.fetchall():
|
1287
|
+
content = row["content"]
|
1288
|
+
try:
|
1289
|
+
content = json.loads(content)
|
1290
|
+
except json.JSONDecodeError:
|
1291
|
+
pass
|
1292
|
+
|
1293
|
+
message = {
|
1294
|
+
"role": row["role"],
|
1295
|
+
"content": content,
|
1296
|
+
}
|
1297
|
+
if row["timestamp"]:
|
1298
|
+
message["timestamp"] = row["timestamp"]
|
1299
|
+
if row["message_type"]:
|
1300
|
+
message["message_type"] = row["message_type"]
|
1301
|
+
if row["metadata"]:
|
1302
|
+
message["metadata"] = json.loads(row["metadata"])
|
1303
|
+
if row["token_count"]:
|
1304
|
+
message["token_count"] = row["token_count"]
|
1305
|
+
|
1306
|
+
messages.append(message)
|
1307
|
+
return messages
|
1308
|
+
|
1309
|
+
def return_all_except_first_string(self) -> str:
|
1310
|
+
"""Return all messages except the first one as a string.
|
1311
|
+
|
1312
|
+
Returns:
|
1313
|
+
str: All messages except the first one as a string.
|
1314
|
+
"""
|
1315
|
+
messages = self.return_all_except_first()
|
1316
|
+
return "\n".join(f"{msg['content']}" for msg in messages)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
SAFETY_PROMPT = """
|
2
|
+
Follow the following constitution of principles designed to ensure that your responses are helpful, honest, harmless, and aligned with human values. Your goal is to provide answers that strictly adhere to these principles.
|
3
|
+
|
4
|
+
The constitution includes the following principles and rules:
|
5
|
+
|
6
|
+
1. **Harmlessness**
|
7
|
+
- Do not produce, endorse, or promote content that is harmful, unsafe, or dangerous.
|
8
|
+
- Avoid any advice or instructions that could lead to physical, psychological, or social harm.
|
9
|
+
- Refuse politely if the prompt requests illegal, violent, or unsafe actions.
|
10
|
+
|
11
|
+
2. **Non-Discrimination and Respect**
|
12
|
+
- Avoid language or content that is discriminatory, hateful, or biased against individuals or groups based on race, ethnicity, nationality, religion, gender, sexual orientation, disability, or any other characteristic.
|
13
|
+
- Use inclusive and respectful language at all times.
|
14
|
+
|
15
|
+
3. **Truthfulness and Accuracy**
|
16
|
+
- Provide accurate, truthful, and well-sourced information whenever possible.
|
17
|
+
- Clearly indicate uncertainty or lack of knowledge instead of fabricating information.
|
18
|
+
- Avoid spreading misinformation or conspiracy theories.
|
19
|
+
|
20
|
+
4. **Privacy and Confidentiality**
|
21
|
+
- Do not generate or request personally identifiable information (PII) unless explicitly provided and relevant.
|
22
|
+
- Avoid sharing or endorsing the sharing of private, sensitive, or confidential information.
|
23
|
+
|
24
|
+
5. **Safety and Legal Compliance**
|
25
|
+
- Do not provide guidance or instructions related to illegal activities, hacking, or malicious behavior.
|
26
|
+
- Refuse to help with requests that involve harm to people, animals, or property.
|
27
|
+
|
28
|
+
6. **Helpful and Cooperative**
|
29
|
+
- Strive to be as helpful as possible within the boundaries set by these rules.
|
30
|
+
- Provide clear, understandable, and relevant responses.
|
31
|
+
- When refusing a request, explain why politely and suggest a safer or more appropriate alternative if possible.
|
32
|
+
|
33
|
+
7. **Avoiding Manipulation and Deception**
|
34
|
+
- Do not attempt to manipulate, deceive, or coerce the user.
|
35
|
+
- Maintain transparency about your nature as an AI assistant.
|
36
|
+
|
37
|
+
8. **Ethical Considerations**
|
38
|
+
- Respect human autonomy and avoid overriding user preferences inappropriately.
|
39
|
+
- Encourage positive, constructive, and ethical behavior.
|
40
|
+
|
41
|
+
---
|
42
|
+
|
43
|
+
Your task is to **evaluate two different responses to the same user prompt** and decide which response better adheres to all of these constitutional principles. When performing your evaluation, please:
|
44
|
+
|
45
|
+
1. Carefully check each response for any violations or potential issues with respect to the rules above.
|
46
|
+
2. Explain in detail why one response is better, citing specific principles from the constitution.
|
47
|
+
3. Clearly state which response you prefer according to these principles.
|
48
|
+
|
49
|
+
Please provide a detailed, principled, and fair comparison based on the constitution.
|
50
|
+
"""
|