solace-agent-mesh 0.1.3__py3-none-any.whl → 0.2.0__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 solace-agent-mesh might be problematic. Click here for more details.
- solace_agent_mesh/agents/global/actions/plantuml_diagram.py +9 -2
- solace_agent_mesh/agents/global/actions/plotly_graph.py +38 -40
- solace_agent_mesh/agents/web_request/actions/do_web_request.py +34 -33
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/cli/commands/add/gateway.py +162 -9
- solace_agent_mesh/cli/commands/build.py +0 -1
- solace_agent_mesh/cli/commands/init/create_other_project_files_step.py +52 -1
- solace_agent_mesh/cli/commands/plugin/build.py +11 -2
- solace_agent_mesh/cli/config.py +4 -0
- solace_agent_mesh/cli/utils.py +7 -2
- solace_agent_mesh/common/constants.py +10 -0
- solace_agent_mesh/common/utils.py +16 -11
- solace_agent_mesh/configs/service_embedding.yaml +1 -1
- solace_agent_mesh/configs/service_llm.yaml +1 -1
- solace_agent_mesh/gateway/components/gateway_base.py +7 -1
- solace_agent_mesh/gateway/components/gateway_input.py +8 -5
- solace_agent_mesh/gateway/components/gateway_output.py +12 -3
- solace_agent_mesh/orchestrator/components/orchestrator_stimulus_processor_component.py +23 -5
- solace_agent_mesh/orchestrator/orchestrator_prompt.py +155 -35
- solace_agent_mesh/services/file_service/file_service.py +5 -0
- solace_agent_mesh/services/file_service/file_service_constants.py +1 -1
- solace_agent_mesh/services/file_service/file_transformations.py +11 -1
- solace_agent_mesh/services/file_service/file_utils.py +2 -0
- solace_agent_mesh/services/history_service/history_providers/base_history_provider.py +21 -46
- solace_agent_mesh/services/history_service/history_providers/file_history_provider.py +74 -0
- solace_agent_mesh/services/history_service/history_providers/index.py +40 -0
- solace_agent_mesh/services/history_service/history_providers/memory_history_provider.py +19 -156
- solace_agent_mesh/services/history_service/history_providers/mongodb_history_provider.py +66 -0
- solace_agent_mesh/services/history_service/history_providers/redis_history_provider.py +40 -140
- solace_agent_mesh/services/history_service/history_providers/sql_history_provider.py +93 -0
- solace_agent_mesh/services/history_service/history_service.py +315 -41
- solace_agent_mesh/services/history_service/long_term_memory/__init__.py +0 -0
- solace_agent_mesh/services/history_service/long_term_memory/long_term_memory.py +399 -0
- solace_agent_mesh/services/llm_service/components/llm_request_component.py +19 -0
- solace_agent_mesh/templates/gateway-config-template.yaml +2 -1
- solace_agent_mesh/templates/gateway-default-config.yaml +3 -3
- solace_agent_mesh/templates/plugin-gateway-default-config.yaml +29 -0
- solace_agent_mesh/templates/rest-api-default-config.yaml +2 -1
- solace_agent_mesh/templates/slack-default-config.yaml +1 -1
- solace_agent_mesh/templates/web-default-config.yaml +2 -1
- {solace_agent_mesh-0.1.3.dist-info → solace_agent_mesh-0.2.0.dist-info}/METADATA +4 -3
- {solace_agent_mesh-0.1.3.dist-info → solace_agent_mesh-0.2.0.dist-info}/RECORD +45 -38
- {solace_agent_mesh-0.1.3.dist-info → solace_agent_mesh-0.2.0.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-0.1.3.dist-info → solace_agent_mesh-0.2.0.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-0.1.3.dist-info → solace_agent_mesh-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,113 +1,315 @@
|
|
|
1
1
|
import time
|
|
2
2
|
import importlib
|
|
3
|
-
|
|
3
|
+
import threading
|
|
4
|
+
from typing import Union, Tuple
|
|
4
5
|
|
|
5
6
|
from solace_ai_connector.common.log import log
|
|
6
7
|
|
|
7
|
-
from ...common.time import ONE_HOUR, FIVE_MINUTES
|
|
8
|
+
from ...common.time import ONE_HOUR, FIVE_MINUTES, ONE_DAY
|
|
9
|
+
from ...common.constants import HISTORY_MEMORY_ROLE, HISTORY_ACTION_ROLE, HISTORY_USER_ROLE, HISTORY_ASSISTANT_ROLE
|
|
8
10
|
from ..common import AutoExpiry, AutoExpirySingletonMeta
|
|
9
|
-
from .history_providers.
|
|
10
|
-
from .history_providers.redis_history_provider import RedisHistoryProvider
|
|
11
|
+
from .history_providers.index import HistoryProviderFactory
|
|
11
12
|
from .history_providers.base_history_provider import BaseHistoryProvider
|
|
12
|
-
|
|
13
|
-
HISTORY_PROVIDERS = {
|
|
14
|
-
"redis": RedisHistoryProvider,
|
|
15
|
-
"memory": MemoryHistoryProvider,
|
|
16
|
-
}
|
|
17
|
-
|
|
13
|
+
from .long_term_memory.long_term_memory import LongTermMemory
|
|
18
14
|
|
|
19
15
|
DEFAULT_PROVIDER = "memory"
|
|
20
16
|
|
|
21
17
|
DEFAULT_MAX_TURNS = 40
|
|
22
18
|
DEFAULT_MAX_CHARACTERS = 50_000
|
|
19
|
+
DEFAULT_SUMMARY_TIME_TO_LIVE = ONE_DAY * 5
|
|
23
20
|
|
|
24
21
|
DEFAULT_HISTORY_POLICY = {
|
|
25
22
|
"max_turns": DEFAULT_MAX_TURNS,
|
|
26
23
|
"max_characters": DEFAULT_MAX_CHARACTERS,
|
|
27
|
-
"enforce_alternate_message_roles":
|
|
24
|
+
"enforce_alternate_message_roles": True,
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
|
|
31
28
|
# HistoryService class - Manages history storage and retrieval
|
|
32
29
|
class HistoryService(AutoExpiry, metaclass=AutoExpirySingletonMeta):
|
|
33
30
|
history_provider: BaseHistoryProvider
|
|
31
|
+
long_term_memory_store: BaseHistoryProvider
|
|
32
|
+
long_term_memory_service: LongTermMemory
|
|
34
33
|
|
|
35
34
|
def __init__(self, config={}, identifier=None):
|
|
35
|
+
"""
|
|
36
|
+
Initialize the history service.
|
|
37
|
+
"""
|
|
36
38
|
self.identifier = identifier
|
|
37
39
|
self.config = config
|
|
38
|
-
self.provider_type = self.config.get("type", DEFAULT_PROVIDER)
|
|
39
40
|
self.time_to_live = self.config.get("time_to_live", ONE_HOUR)
|
|
41
|
+
self.use_long_term_memory = self.config.get("enable_long_term_memory", False)
|
|
40
42
|
self.expiration_check_interval = self.config.get(
|
|
41
43
|
"expiration_check_interval", FIVE_MINUTES
|
|
42
44
|
)
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
"module_path"
|
|
46
|
-
):
|
|
47
|
-
raise ValueError(
|
|
48
|
-
f"Unsupported history provider type: {self.provider_type}. No module_path provided."
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
history_policy = {
|
|
46
|
+
self.history_policy = {
|
|
52
47
|
**DEFAULT_HISTORY_POLICY,
|
|
53
48
|
**self.config.get("history_policy", {}),
|
|
54
49
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
self.
|
|
58
|
-
|
|
50
|
+
|
|
51
|
+
self.history_provider = self._get_history_provider(
|
|
52
|
+
self.config.get("type", DEFAULT_PROVIDER),
|
|
53
|
+
self.config.get("module_path"),
|
|
54
|
+
self.history_policy
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if self.use_long_term_memory:
|
|
58
|
+
# Setting up the long-term memory service
|
|
59
|
+
self.long_term_memory_config = self.config.get("long_term_memory_config", {})
|
|
60
|
+
if not self.long_term_memory_config.get("llm_config"):
|
|
61
|
+
raise ValueError("Missing required configuration for Long-Term Memory provider, Missing 'model' or 'api_key' in 'history_policy.long_term_memory_config.llm_config'.")
|
|
62
|
+
self.long_term_memory_service = LongTermMemory(self.long_term_memory_config.get("llm_config"))
|
|
63
|
+
|
|
64
|
+
# Setting up the long-term memory store
|
|
65
|
+
store_config = self.long_term_memory_config.get("store_config", {})
|
|
66
|
+
self.long_term_memory_store = self._get_history_provider(
|
|
67
|
+
store_config.get("type", DEFAULT_PROVIDER),
|
|
68
|
+
store_config.get("module_path"),
|
|
69
|
+
store_config
|
|
59
70
|
)
|
|
71
|
+
|
|
72
|
+
# Start the background thread for auto-expiry
|
|
73
|
+
self._start_auto_expiry_thread(self.expiration_check_interval)
|
|
74
|
+
|
|
75
|
+
def _get_history_provider(self, provider_type:str, module_path:str="", config:dict={}):
|
|
76
|
+
"""
|
|
77
|
+
Get the history provider based on the provider type.
|
|
78
|
+
"""
|
|
79
|
+
if HistoryProviderFactory.has_provider(provider_type):
|
|
80
|
+
return HistoryProviderFactory.get_provider_class(provider_type)(config)
|
|
60
81
|
else:
|
|
82
|
+
if not module_path:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"Unsupported history provider type: {provider_type}. No module_path provided."
|
|
85
|
+
)
|
|
61
86
|
try:
|
|
62
|
-
# Load the provider from the module path
|
|
63
87
|
module_name = self.provider_type
|
|
64
|
-
module_path = self.config.get("module_path")
|
|
65
88
|
module = importlib.import_module(module_path, package=__package__)
|
|
66
89
|
history_class = getattr(module, module_name)
|
|
67
90
|
if not issubclass(history_class, BaseHistoryProvider):
|
|
68
91
|
raise ValueError(
|
|
69
92
|
f"History provider class {history_class} does not inherit from BaseHistoryProvider"
|
|
70
93
|
)
|
|
71
|
-
|
|
94
|
+
return history_class(config)
|
|
72
95
|
except Exception as e:
|
|
73
96
|
raise ImportError("Unable to load component: " + str(e)) from e
|
|
74
97
|
|
|
75
|
-
# Start the background thread for auto-expiry
|
|
76
|
-
self._start_auto_expiry_thread(self.expiration_check_interval)
|
|
77
|
-
|
|
78
98
|
def _delete_expired_items(self):
|
|
79
99
|
"""Checks all history entries and deletes those that have exceeded max_time_to_live."""
|
|
80
100
|
current_time = time.time()
|
|
81
101
|
sessions = self.history_provider.get_all_sessions()
|
|
82
102
|
for session_id in sessions:
|
|
83
|
-
session = self.history_provider.
|
|
103
|
+
session = self.history_provider.get_session(session_id)
|
|
84
104
|
if not session:
|
|
85
105
|
continue
|
|
86
106
|
elapsed_time = current_time - session["last_active_time"]
|
|
87
107
|
if elapsed_time > self.time_to_live:
|
|
88
|
-
self.
|
|
89
|
-
log.debug(
|
|
108
|
+
self.clear_history(session_id)
|
|
109
|
+
log.debug("History for session %s has expired", session_id)
|
|
110
|
+
|
|
111
|
+
def _get_empty_history_entry(self):
|
|
112
|
+
"""
|
|
113
|
+
Get an empty history entry.
|
|
114
|
+
"""
|
|
115
|
+
return {
|
|
116
|
+
"history": [],
|
|
117
|
+
"files": [],
|
|
118
|
+
"summary": "",
|
|
119
|
+
"last_active_time": time.time(),
|
|
120
|
+
"num_characters": 0,
|
|
121
|
+
"num_turns": 0,
|
|
122
|
+
}
|
|
123
|
+
|
|
90
124
|
|
|
91
|
-
def
|
|
125
|
+
def _merge_assistant_with_actions(self, assistant_message:str, history:list) -> Tuple[str, list]:
|
|
126
|
+
"""
|
|
127
|
+
Merge assistant message with the actions called to generate the response.
|
|
128
|
+
"""
|
|
129
|
+
actions_called = []
|
|
130
|
+
# Looping reversely to get the most recent action
|
|
131
|
+
index = len(history)
|
|
132
|
+
for entry in reversed(history):
|
|
133
|
+
if entry["role"] == HISTORY_ACTION_ROLE:
|
|
134
|
+
actions_called.append(entry["content"])
|
|
135
|
+
index -= 1
|
|
136
|
+
else:
|
|
137
|
+
break
|
|
138
|
+
if actions_called:
|
|
139
|
+
actions_called_str = ""
|
|
140
|
+
for action_called in reversed(actions_called):
|
|
141
|
+
actions_called_str += (
|
|
142
|
+
f"\n - Agent: {action_called.get('agent_name')}"
|
|
143
|
+
f"\n Action: {action_called.get('action_name')}"
|
|
144
|
+
f"\n Action Parameters: {action_called.get('action_params')}"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
actions_called_prompt = (
|
|
148
|
+
"<message_metadata>\n"
|
|
149
|
+
"[Following actions were called to generate this response:]"
|
|
150
|
+
f"{actions_called_str}"
|
|
151
|
+
"\n</message_metadata>\n\n"
|
|
152
|
+
)
|
|
153
|
+
assistant_message = f"{actions_called_prompt}{assistant_message}"
|
|
154
|
+
|
|
155
|
+
return assistant_message, history[:index]
|
|
156
|
+
|
|
157
|
+
def _filter_actions(self, history:list) -> list:
|
|
158
|
+
"""
|
|
159
|
+
Filter out action entries from the history.
|
|
160
|
+
"""
|
|
161
|
+
return [entry for entry in history if entry["role"] != HISTORY_ACTION_ROLE]
|
|
162
|
+
|
|
163
|
+
def store_history(self, session_id: str, role: str, content: Union[str, dict], other_history_props: dict = {}):
|
|
92
164
|
"""
|
|
93
165
|
Store a new entry in the history.
|
|
94
166
|
|
|
95
167
|
:param session_id: The session identifier.
|
|
96
168
|
:param role: The role of the entry to be stored in the history.
|
|
97
169
|
:param content: The content of the entry to be stored in the history.
|
|
170
|
+
:param other_history_props: Other history properties such as user identifier.
|
|
98
171
|
"""
|
|
99
172
|
if not content:
|
|
100
173
|
return
|
|
101
|
-
|
|
174
|
+
|
|
175
|
+
user_identity = other_history_props.get("identity", session_id)
|
|
176
|
+
|
|
177
|
+
history = self.history_provider.get_session(session_id).copy()
|
|
178
|
+
if not history:
|
|
179
|
+
history = self._get_empty_history_entry()
|
|
180
|
+
|
|
181
|
+
if role == HISTORY_ASSISTANT_ROLE:
|
|
182
|
+
content, history["history"] = self._merge_assistant_with_actions(content, history["history"])
|
|
183
|
+
elif role == HISTORY_USER_ROLE:
|
|
184
|
+
history["history"] = self._filter_actions(history["history"])
|
|
185
|
+
|
|
186
|
+
if (
|
|
187
|
+
self.history_policy.get("enforce_alternate_message_roles")
|
|
188
|
+
and history["num_turns"] > 0
|
|
189
|
+
# Check if the last entry was by the same role
|
|
190
|
+
and history["history"]
|
|
191
|
+
and history["history"][-1]["role"] == role
|
|
192
|
+
):
|
|
193
|
+
# Append to last entry
|
|
194
|
+
history["history"][-1]["content"] += "\n\n" + content
|
|
195
|
+
else:
|
|
196
|
+
# Add the new entry
|
|
197
|
+
history["history"].append(
|
|
198
|
+
{"role": role, "content": content}
|
|
199
|
+
)
|
|
200
|
+
# Update the number of turns
|
|
201
|
+
history["num_turns"] += 1
|
|
202
|
+
|
|
203
|
+
# Update the length
|
|
204
|
+
history["num_characters"] += len(str(content))
|
|
205
|
+
# Update the last active time
|
|
206
|
+
history["last_active_time"] = time.time()
|
|
102
207
|
|
|
103
|
-
|
|
208
|
+
# Extract memory from the last 2 messages if use long term memory is enabled
|
|
209
|
+
if self.use_long_term_memory and role == HISTORY_USER_ROLE and len(history["history"]) > 2:
|
|
210
|
+
recent_messages = history["history"][-3:-1].copy()
|
|
211
|
+
def background_task():
|
|
212
|
+
memory = self.long_term_memory_service.extract_memory_from_chat(recent_messages)
|
|
213
|
+
|
|
214
|
+
if memory and (memory.get("facts") or memory.get("instructions") or memory.get("update_notes")):
|
|
215
|
+
old_memory = self.long_term_memory_store.get_session(user_identity).get("memory", {})
|
|
216
|
+
updated_memory = self.long_term_memory_service.update_user_memory(old_memory, memory)
|
|
217
|
+
self.long_term_memory_store.update_session(user_identity, {
|
|
218
|
+
"memory": updated_memory
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
threading.Thread(target=background_task).start()
|
|
222
|
+
|
|
223
|
+
# Check if active session history requires truncation
|
|
224
|
+
if ((self.history_policy.get("max_characters") and (history["num_characters"] > self.history_policy.get("max_characters")))
|
|
225
|
+
or history["num_turns"] > self.history_policy.get("max_turns")):
|
|
226
|
+
|
|
227
|
+
cut_off_index = 0
|
|
228
|
+
if history["num_turns"] > self.history_policy.get("max_turns"):
|
|
229
|
+
cut_off_index = max(0, int(self.history_policy.get("max_turns") * 0.5)) # 40% of max_turns
|
|
230
|
+
|
|
231
|
+
if self.history_policy.get("max_characters") and (history["num_characters"] > self.history_policy.get("max_characters")):
|
|
232
|
+
index = 0
|
|
233
|
+
characters = 0
|
|
234
|
+
while characters < self.history_policy.get("max_characters") and index < len(history["history"]) - 1:
|
|
235
|
+
characters += len(str(history["history"][index]["content"]))
|
|
236
|
+
index += 1
|
|
237
|
+
cut_off_index = max(cut_off_index, index)
|
|
238
|
+
|
|
239
|
+
cut_off_index = min(cut_off_index, len(history["history"])) # Ensure cut_off_index is within bounds
|
|
240
|
+
|
|
241
|
+
if self.use_long_term_memory:
|
|
242
|
+
cut_of_history = history["history"][:cut_off_index].copy()
|
|
243
|
+
def background_summary_task():
|
|
244
|
+
summary = self.long_term_memory_service.summarize_chat(cut_of_history)
|
|
245
|
+
updated_summary = self.long_term_memory_service.update_summary(history["summary"], summary)
|
|
246
|
+
|
|
247
|
+
fetched_history = self.history_provider.get_session(session_id).copy()
|
|
248
|
+
if fetched_history:
|
|
249
|
+
fetched_history["summary"] = updated_summary
|
|
250
|
+
self.history_provider.store_session(session_id, fetched_history)
|
|
251
|
+
|
|
252
|
+
threading.Thread(target=background_summary_task).start()
|
|
253
|
+
|
|
254
|
+
history["history"] = history["history"][cut_off_index:]
|
|
255
|
+
history["num_characters"] = sum(len(str(entry["content"])) for entry in history["history"])
|
|
256
|
+
history["num_turns"] = len(history["history"])
|
|
257
|
+
history["last_active_time"] = time.time()
|
|
258
|
+
|
|
259
|
+
# Update the session history
|
|
260
|
+
return self.history_provider.store_session(session_id, history)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def get_history(self, session_id:str, other_history_props: dict = {}) -> list:
|
|
104
264
|
"""
|
|
105
265
|
Retrieve the entire history.
|
|
106
266
|
|
|
107
267
|
:param session_id: The session identifier.
|
|
268
|
+
:param other_history_props: Other history properties such as user identifier.
|
|
108
269
|
:return: The complete history.
|
|
109
270
|
"""
|
|
110
|
-
|
|
271
|
+
history = self.history_provider.get_session(session_id)
|
|
272
|
+
messages = history.get("history", [])
|
|
273
|
+
|
|
274
|
+
if self.use_long_term_memory:
|
|
275
|
+
user_identity = other_history_props.get("identity", session_id)
|
|
276
|
+
stored_memory = self.long_term_memory_store.get_session(user_identity)
|
|
277
|
+
if stored_memory:
|
|
278
|
+
long_term_memory = self.long_term_memory_service.retrieve_user_memory(stored_memory.get("memory", {}), history.get("summary", ""))
|
|
279
|
+
if long_term_memory:
|
|
280
|
+
return [
|
|
281
|
+
{
|
|
282
|
+
"role": HISTORY_MEMORY_ROLE,
|
|
283
|
+
"content": long_term_memory
|
|
284
|
+
},
|
|
285
|
+
*messages
|
|
286
|
+
]
|
|
287
|
+
|
|
288
|
+
return messages
|
|
289
|
+
|
|
290
|
+
def store_actions(self, session_id:str, actions:list):
|
|
291
|
+
"""
|
|
292
|
+
Store an action in the history.
|
|
293
|
+
|
|
294
|
+
:param session_id: The session identifier.
|
|
295
|
+
:param actions: The actions to be stored in the history.
|
|
296
|
+
"""
|
|
297
|
+
if not actions:
|
|
298
|
+
return
|
|
299
|
+
|
|
300
|
+
history = self.history_provider.get_session(session_id).copy()
|
|
301
|
+
if not history:
|
|
302
|
+
history = self._get_empty_history_entry()
|
|
303
|
+
|
|
304
|
+
for action in actions:
|
|
305
|
+
history["history"].append(
|
|
306
|
+
{"role": HISTORY_ACTION_ROLE, "content": action}
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
history["last_active_time"] = time.time()
|
|
310
|
+
|
|
311
|
+
return self.history_provider.store_session(session_id, history)
|
|
312
|
+
|
|
111
313
|
|
|
112
314
|
def store_file(self, session_id:str, file:dict):
|
|
113
315
|
"""
|
|
@@ -118,7 +320,20 @@ class HistoryService(AutoExpiry, metaclass=AutoExpirySingletonMeta):
|
|
|
118
320
|
"""
|
|
119
321
|
if not file:
|
|
120
322
|
return
|
|
121
|
-
|
|
323
|
+
|
|
324
|
+
history = self.history_provider.get_session(session_id).copy()
|
|
325
|
+
if not history:
|
|
326
|
+
history = self._get_empty_history_entry()
|
|
327
|
+
|
|
328
|
+
# Check duplicate
|
|
329
|
+
for f in history["files"]:
|
|
330
|
+
if f.get("url") and f.get("url") == file.get("url"):
|
|
331
|
+
return
|
|
332
|
+
|
|
333
|
+
history["files"].append(file)
|
|
334
|
+
history["last_active_time"] = time.time()
|
|
335
|
+
|
|
336
|
+
return self.history_provider.store_session(session_id, history)
|
|
122
337
|
|
|
123
338
|
def get_files(self, session_id:str) -> list:
|
|
124
339
|
"""
|
|
@@ -127,13 +342,72 @@ class HistoryService(AutoExpiry, metaclass=AutoExpirySingletonMeta):
|
|
|
127
342
|
:param session_id: The session identifier.
|
|
128
343
|
:return: The files for the session.
|
|
129
344
|
"""
|
|
130
|
-
|
|
345
|
+
history = self.history_provider.get_session(session_id)
|
|
346
|
+
if not history:
|
|
347
|
+
return []
|
|
348
|
+
|
|
349
|
+
files = []
|
|
350
|
+
current_time = time.time()
|
|
351
|
+
all_files = history["files"].copy()
|
|
352
|
+
|
|
353
|
+
for file in all_files:
|
|
354
|
+
expiration_timestamp = file.get("expiration_timestamp")
|
|
355
|
+
if expiration_timestamp and current_time > expiration_timestamp:
|
|
356
|
+
history["files"].remove(file)
|
|
357
|
+
continue
|
|
358
|
+
files.append(file)
|
|
359
|
+
|
|
360
|
+
self.history_provider.store_session(session_id, history)
|
|
361
|
+
return files
|
|
131
362
|
|
|
132
|
-
|
|
363
|
+
|
|
364
|
+
def clear_history(self, session_id:str, keep_levels=0, clear_files=True):
|
|
133
365
|
"""
|
|
134
366
|
Clear the history and files, optionally keeping a specified number of recent entries.
|
|
135
367
|
|
|
136
368
|
:param session_id: The session identifier.
|
|
137
369
|
:param keep_levels: Number of most recent history entries to keep. Default is 0 (clear all).
|
|
370
|
+
:param other_history_props: Other history properties such as user identifier.
|
|
371
|
+
:param clear_files: Whether to clear associated files. Default is True.
|
|
138
372
|
"""
|
|
139
|
-
|
|
373
|
+
|
|
374
|
+
history = self.history_provider.get_session(session_id).copy()
|
|
375
|
+
if not history:
|
|
376
|
+
return
|
|
377
|
+
|
|
378
|
+
if history.get("history") or (clear_files and history.get("files")):
|
|
379
|
+
cut_off_index = max(0, len(history["history"]) - keep_levels)
|
|
380
|
+
cut_off_history = history["history"][:cut_off_index]
|
|
381
|
+
|
|
382
|
+
if self.use_long_term_memory and cut_off_history:
|
|
383
|
+
def background_task():
|
|
384
|
+
summary = self.long_term_memory_service.summarize_chat(cut_off_history)
|
|
385
|
+
updated_summary = self.long_term_memory_service.update_summary(history["summary"], summary)
|
|
386
|
+
|
|
387
|
+
fetched_history = self.history_provider.get_session(session_id).copy()
|
|
388
|
+
if fetched_history:
|
|
389
|
+
fetched_history["summary"] = updated_summary
|
|
390
|
+
self.history_provider.store_session(session_id, fetched_history)
|
|
391
|
+
threading.Thread(target=background_task).start()
|
|
392
|
+
|
|
393
|
+
history["history"] = [] if keep_levels <= 0 else history["history"][-keep_levels:]
|
|
394
|
+
history["num_turns"] = keep_levels
|
|
395
|
+
history["num_characters"] = sum(len(str(entry["content"])) for entry in history["history"])
|
|
396
|
+
history["last_active_time"] = time.time()
|
|
397
|
+
|
|
398
|
+
if clear_files:
|
|
399
|
+
history["files"] = []
|
|
400
|
+
|
|
401
|
+
return self.history_provider.store_session(session_id, history)
|
|
402
|
+
|
|
403
|
+
# Summaries get cleared at a longer expiry time
|
|
404
|
+
elif self.use_long_term_memory and history.get("summary"):
|
|
405
|
+
elapsed_time = time.time() - history["last_active_time"]
|
|
406
|
+
summary_ttl = self.long_term_memory_config.get("summary_time_to_live", DEFAULT_SUMMARY_TIME_TO_LIVE)
|
|
407
|
+
if elapsed_time > summary_ttl:
|
|
408
|
+
return self.history_provider.delete_session(session_id)
|
|
409
|
+
|
|
410
|
+
# Delete the session if it has no chat history, files or summary
|
|
411
|
+
else:
|
|
412
|
+
return self.history_provider.delete_session(session_id)
|
|
413
|
+
|
|
File without changes
|