bedrock-agentcore-starter-toolkit 0.1.3__py3-none-any.whl → 0.1.5__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 bedrock-agentcore-starter-toolkit might be problematic. Click here for more details.
- bedrock_agentcore_starter_toolkit/cli/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/cli/cli.py +2 -0
- bedrock_agentcore_starter_toolkit/cli/import_agent/README.md +35 -0
- bedrock_agentcore_starter_toolkit/cli/import_agent/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/cli/import_agent/agent_info.py +230 -0
- bedrock_agentcore_starter_toolkit/cli/import_agent/commands.py +518 -0
- bedrock_agentcore_starter_toolkit/operations/gateway/client.py +2 -2
- bedrock_agentcore_starter_toolkit/services/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/assets/memory_manager_template.py +207 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/assets/requirements_langchain.j2 +9 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/assets/requirements_strands.j2 +5 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/assets/template_fixtures_merged.json +1102 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/scripts/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/scripts/base_bedrock_translate.py +1668 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/scripts/bedrock_to_langchain.py +382 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/scripts/bedrock_to_strands.py +374 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/utils.py +417 -0
- bedrock_agentcore_starter_toolkit/services/runtime.py +13 -2
- bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2 +1 -1
- bedrock_agentcore_starter_toolkit/utils/runtime/templates/execution_role_policy.json.j2 +2 -1
- {bedrock_agentcore_starter_toolkit-0.1.3.dist-info → bedrock_agentcore_starter_toolkit-0.1.5.dist-info}/METADATA +22 -2
- {bedrock_agentcore_starter_toolkit-0.1.3.dist-info → bedrock_agentcore_starter_toolkit-0.1.5.dist-info}/RECORD +27 -11
- {bedrock_agentcore_starter_toolkit-0.1.3.dist-info → bedrock_agentcore_starter_toolkit-0.1.5.dist-info}/WHEEL +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.3.dist-info → bedrock_agentcore_starter_toolkit-0.1.5.dist-info}/entry_points.txt +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.3.dist-info → bedrock_agentcore_starter_toolkit-0.1.5.dist-info}/licenses/LICENSE.txt +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.3.dist-info → bedrock_agentcore_starter_toolkit-0.1.5.dist-info}/licenses/NOTICE.txt +0 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# pylint: disable=line-too-long
|
|
2
|
+
"""Long Term Memory Manager for generated agents.
|
|
3
|
+
|
|
4
|
+
This module provides a custom memory manager that mimics the functionality of Bedrock Agents
|
|
5
|
+
Long Term Memory and Sessions.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import weakref
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from typing import Any, Dict, List, Set
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LongTermMemoryManager:
|
|
17
|
+
"""Custom Memory Manager to have equivalent functionality with Bedrock Agents Long Term Memory and Sessions."""
|
|
18
|
+
|
|
19
|
+
# Class variable to keep track of all instances
|
|
20
|
+
_instances: Set[weakref.ref] = set()
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
llm_summarizer,
|
|
25
|
+
storage_path: str = "output",
|
|
26
|
+
max_sessions: int = 10,
|
|
27
|
+
summarization_prompt: str = None,
|
|
28
|
+
max_days: int = 30,
|
|
29
|
+
platform: str = "langchain",
|
|
30
|
+
):
|
|
31
|
+
"""Initialize the LongTermMemoryManager."""
|
|
32
|
+
self.llm_summarizer = llm_summarizer
|
|
33
|
+
self.storage_path = storage_path
|
|
34
|
+
self.max_sessions = max_sessions
|
|
35
|
+
self.max_days = max_days
|
|
36
|
+
self.current_session_messages = []
|
|
37
|
+
self.summarization_prompt = summarization_prompt
|
|
38
|
+
self._last_memory_update_time = 0
|
|
39
|
+
self.platform = platform
|
|
40
|
+
self._session_ended = False # Track if this instance has ended its session
|
|
41
|
+
|
|
42
|
+
self.session_summaries = self._load_session_summaries()
|
|
43
|
+
|
|
44
|
+
# Register this instance in the class-level instances set
|
|
45
|
+
self._instances.add(weakref.ref(self, self._cleanup_reference))
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def _cleanup_reference(ref):
|
|
49
|
+
"""Callback for when a weak reference is removed."""
|
|
50
|
+
LongTermMemoryManager._instances.discard(ref)
|
|
51
|
+
|
|
52
|
+
def _load_session_summaries(self) -> List[Dict[str, Any]]:
|
|
53
|
+
"""Load all stored session summaries."""
|
|
54
|
+
summary_file = self.storage_path
|
|
55
|
+
if os.path.exists(summary_file):
|
|
56
|
+
with open(summary_file, "r") as f:
|
|
57
|
+
return json.load(f)
|
|
58
|
+
return []
|
|
59
|
+
|
|
60
|
+
def _save_session_summaries(self):
|
|
61
|
+
summary_file = self.storage_path
|
|
62
|
+
with open(summary_file, "a+", encoding="utf-8") as f:
|
|
63
|
+
f.truncate(0)
|
|
64
|
+
json.dump(self.session_summaries, f)
|
|
65
|
+
self._last_memory_update_time = datetime.now().timestamp()
|
|
66
|
+
|
|
67
|
+
def add_message(self, message: Dict[str, str]):
|
|
68
|
+
"""Add a message to the current session."""
|
|
69
|
+
self.current_session_messages.append(message)
|
|
70
|
+
|
|
71
|
+
def _generate_session_summary(self) -> str:
|
|
72
|
+
try:
|
|
73
|
+
conversation_str = "\n\n".join(
|
|
74
|
+
[f"{msg['role'].capitalize()}: {msg['content']}" for msg in self.current_session_messages]
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
past_summaries = "\n".join([summary["summary"] for summary in self.session_summaries])
|
|
78
|
+
|
|
79
|
+
summarization_prompt = self.summarization_prompt.replace(
|
|
80
|
+
"$past_conversation_summary$", past_summaries
|
|
81
|
+
).replace("$conversation$", conversation_str)
|
|
82
|
+
|
|
83
|
+
if self.platform == "langchain":
|
|
84
|
+
summary_response = self.llm_summarizer.invoke(summarization_prompt).content
|
|
85
|
+
else:
|
|
86
|
+
|
|
87
|
+
def inference(model, messages, system_prompt=""):
|
|
88
|
+
async def run_inference():
|
|
89
|
+
results = []
|
|
90
|
+
async for event in model.stream(messages=messages, system_prompt=system_prompt):
|
|
91
|
+
results.append(event)
|
|
92
|
+
return results
|
|
93
|
+
|
|
94
|
+
response = asyncio.run(run_inference())
|
|
95
|
+
|
|
96
|
+
text = ""
|
|
97
|
+
for chunk in response:
|
|
98
|
+
if "contentBlockDelta" not in chunk:
|
|
99
|
+
continue
|
|
100
|
+
text += chunk["contentBlockDelta"].get("delta", {}).get("text", "")
|
|
101
|
+
|
|
102
|
+
return text
|
|
103
|
+
|
|
104
|
+
summary_response = inference(
|
|
105
|
+
self.llm_summarizer, messages=[{"role": "user", "content": [{"text": summarization_prompt}]}]
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return summary_response
|
|
109
|
+
except Exception as e:
|
|
110
|
+
print(f"Error generating summary: {str(e)}")
|
|
111
|
+
message = self.current_session_messages[-1]["content"] if self.current_session_messages else "No messages"
|
|
112
|
+
return f"Session summary generation failed. Last message: {message}"
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def _cleanup_instance(cls):
|
|
116
|
+
"""Remove dead references from the instances set."""
|
|
117
|
+
cls._instances = {ref for ref in cls._instances if ref() is not None}
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def get_active_instances_count(cls):
|
|
121
|
+
"""Return the number of active memory manager instances."""
|
|
122
|
+
# Clean up any dead references first
|
|
123
|
+
cls._instances = {ref for ref in cls._instances if ref() is not None}
|
|
124
|
+
return len(cls._instances)
|
|
125
|
+
|
|
126
|
+
@classmethod
|
|
127
|
+
def get_active_instances(cls):
|
|
128
|
+
"""Return a list of all active memory manager instances."""
|
|
129
|
+
# Clean up any dead references first
|
|
130
|
+
cls._instances = {ref for ref in cls._instances if ref() is not None}
|
|
131
|
+
return [ref() for ref in cls._instances if ref() is not None]
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def end_all_sessions(cls):
|
|
135
|
+
"""End sessions for all active memory manager instances.
|
|
136
|
+
|
|
137
|
+
This is a convenience method that can be called from anywhere to end all sessions.
|
|
138
|
+
"""
|
|
139
|
+
instances = cls.get_active_instances()
|
|
140
|
+
if instances:
|
|
141
|
+
instances[0].end_session()
|
|
142
|
+
|
|
143
|
+
def end_session(self):
|
|
144
|
+
"""End the current session and trigger end_session for all other instances.
|
|
145
|
+
|
|
146
|
+
This ensures that when one agent ends its session, all other agents do the same.
|
|
147
|
+
"""
|
|
148
|
+
# Prevent recursive calls
|
|
149
|
+
if self._session_ended:
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
self._session_ended = True
|
|
153
|
+
|
|
154
|
+
# Process this instance's session
|
|
155
|
+
if self.current_session_messages:
|
|
156
|
+
summary = self._generate_session_summary()
|
|
157
|
+
session_summary = {"timestamp": datetime.now().isoformat(), "summary": summary}
|
|
158
|
+
self.session_summaries.append(session_summary)
|
|
159
|
+
|
|
160
|
+
self.session_summaries = [
|
|
161
|
+
summary
|
|
162
|
+
for summary in self.session_summaries
|
|
163
|
+
if (
|
|
164
|
+
datetime.fromisoformat(session_summary["timestamp"]) - datetime.fromisoformat(summary["timestamp"])
|
|
165
|
+
).days
|
|
166
|
+
<= self.max_days
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
if len(self.session_summaries) > self.max_sessions:
|
|
170
|
+
self.session_summaries = self.session_summaries[-self.max_sessions :]
|
|
171
|
+
|
|
172
|
+
self._save_session_summaries()
|
|
173
|
+
|
|
174
|
+
self.current_session_messages = []
|
|
175
|
+
|
|
176
|
+
# End sessions for all other instances
|
|
177
|
+
for instance_ref in list(self._instances):
|
|
178
|
+
instance = instance_ref()
|
|
179
|
+
if instance is not None and instance is not self and not instance._session_ended:
|
|
180
|
+
try:
|
|
181
|
+
instance.end_session()
|
|
182
|
+
except Exception as e:
|
|
183
|
+
print(f"Error ending session for another instance: {str(e)}")
|
|
184
|
+
|
|
185
|
+
# Reset the flag so this instance can be used again if needed
|
|
186
|
+
self._session_ended = False
|
|
187
|
+
|
|
188
|
+
def get_memory_synopsis(self) -> str:
|
|
189
|
+
"""Get a synopsis of the memory, including all session summaries."""
|
|
190
|
+
return "\n".join([summary["summary"] for summary in self.session_summaries])
|
|
191
|
+
|
|
192
|
+
def has_memory_changed(self) -> bool:
|
|
193
|
+
"""Check if the memory has changed since the last update."""
|
|
194
|
+
summary_file = self.storage_path
|
|
195
|
+
|
|
196
|
+
if not os.path.exists(summary_file):
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
current_mtime = os.path.getmtime(summary_file)
|
|
200
|
+
if current_mtime != self._last_memory_update_time:
|
|
201
|
+
self._last_memory_update_time = current_mtime
|
|
202
|
+
return True
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
def clear_current_session(self):
|
|
206
|
+
"""Clear the current session messages."""
|
|
207
|
+
self.current_session_messages = []
|