agentrun-sdk 0.1.2__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 agentrun-sdk might be problematic. Click here for more details.
- agentrun_operation_sdk/cli/__init__.py +1 -0
- agentrun_operation_sdk/cli/cli.py +19 -0
- agentrun_operation_sdk/cli/common.py +21 -0
- agentrun_operation_sdk/cli/runtime/__init__.py +1 -0
- agentrun_operation_sdk/cli/runtime/commands.py +203 -0
- agentrun_operation_sdk/client/client.py +75 -0
- agentrun_operation_sdk/operations/runtime/__init__.py +8 -0
- agentrun_operation_sdk/operations/runtime/configure.py +101 -0
- agentrun_operation_sdk/operations/runtime/launch.py +82 -0
- agentrun_operation_sdk/operations/runtime/models.py +31 -0
- agentrun_operation_sdk/services/runtime.py +152 -0
- agentrun_operation_sdk/utils/logging_config.py +72 -0
- agentrun_operation_sdk/utils/runtime/config.py +94 -0
- agentrun_operation_sdk/utils/runtime/container.py +280 -0
- agentrun_operation_sdk/utils/runtime/entrypoint.py +203 -0
- agentrun_operation_sdk/utils/runtime/schema.py +56 -0
- agentrun_sdk/__init__.py +7 -0
- agentrun_sdk/agent/__init__.py +25 -0
- agentrun_sdk/agent/agent.py +696 -0
- agentrun_sdk/agent/agent_result.py +46 -0
- agentrun_sdk/agent/conversation_manager/__init__.py +26 -0
- agentrun_sdk/agent/conversation_manager/conversation_manager.py +88 -0
- agentrun_sdk/agent/conversation_manager/null_conversation_manager.py +46 -0
- agentrun_sdk/agent/conversation_manager/sliding_window_conversation_manager.py +179 -0
- agentrun_sdk/agent/conversation_manager/summarizing_conversation_manager.py +252 -0
- agentrun_sdk/agent/state.py +97 -0
- agentrun_sdk/event_loop/__init__.py +9 -0
- agentrun_sdk/event_loop/event_loop.py +499 -0
- agentrun_sdk/event_loop/streaming.py +319 -0
- agentrun_sdk/experimental/__init__.py +4 -0
- agentrun_sdk/experimental/hooks/__init__.py +15 -0
- agentrun_sdk/experimental/hooks/events.py +123 -0
- agentrun_sdk/handlers/__init__.py +10 -0
- agentrun_sdk/handlers/callback_handler.py +70 -0
- agentrun_sdk/hooks/__init__.py +49 -0
- agentrun_sdk/hooks/events.py +80 -0
- agentrun_sdk/hooks/registry.py +247 -0
- agentrun_sdk/models/__init__.py +10 -0
- agentrun_sdk/models/anthropic.py +432 -0
- agentrun_sdk/models/bedrock.py +649 -0
- agentrun_sdk/models/litellm.py +225 -0
- agentrun_sdk/models/llamaapi.py +438 -0
- agentrun_sdk/models/mistral.py +539 -0
- agentrun_sdk/models/model.py +95 -0
- agentrun_sdk/models/ollama.py +357 -0
- agentrun_sdk/models/openai.py +436 -0
- agentrun_sdk/models/sagemaker.py +598 -0
- agentrun_sdk/models/writer.py +449 -0
- agentrun_sdk/multiagent/__init__.py +22 -0
- agentrun_sdk/multiagent/a2a/__init__.py +15 -0
- agentrun_sdk/multiagent/a2a/executor.py +148 -0
- agentrun_sdk/multiagent/a2a/server.py +252 -0
- agentrun_sdk/multiagent/base.py +92 -0
- agentrun_sdk/multiagent/graph.py +555 -0
- agentrun_sdk/multiagent/swarm.py +656 -0
- agentrun_sdk/py.typed +1 -0
- agentrun_sdk/session/__init__.py +18 -0
- agentrun_sdk/session/file_session_manager.py +216 -0
- agentrun_sdk/session/repository_session_manager.py +152 -0
- agentrun_sdk/session/s3_session_manager.py +272 -0
- agentrun_sdk/session/session_manager.py +73 -0
- agentrun_sdk/session/session_repository.py +51 -0
- agentrun_sdk/telemetry/__init__.py +21 -0
- agentrun_sdk/telemetry/config.py +194 -0
- agentrun_sdk/telemetry/metrics.py +476 -0
- agentrun_sdk/telemetry/metrics_constants.py +15 -0
- agentrun_sdk/telemetry/tracer.py +563 -0
- agentrun_sdk/tools/__init__.py +17 -0
- agentrun_sdk/tools/decorator.py +569 -0
- agentrun_sdk/tools/executor.py +137 -0
- agentrun_sdk/tools/loader.py +152 -0
- agentrun_sdk/tools/mcp/__init__.py +13 -0
- agentrun_sdk/tools/mcp/mcp_agent_tool.py +99 -0
- agentrun_sdk/tools/mcp/mcp_client.py +423 -0
- agentrun_sdk/tools/mcp/mcp_instrumentation.py +322 -0
- agentrun_sdk/tools/mcp/mcp_types.py +63 -0
- agentrun_sdk/tools/registry.py +607 -0
- agentrun_sdk/tools/structured_output.py +421 -0
- agentrun_sdk/tools/tools.py +217 -0
- agentrun_sdk/tools/watcher.py +136 -0
- agentrun_sdk/types/__init__.py +5 -0
- agentrun_sdk/types/collections.py +23 -0
- agentrun_sdk/types/content.py +188 -0
- agentrun_sdk/types/event_loop.py +48 -0
- agentrun_sdk/types/exceptions.py +81 -0
- agentrun_sdk/types/guardrails.py +254 -0
- agentrun_sdk/types/media.py +89 -0
- agentrun_sdk/types/session.py +152 -0
- agentrun_sdk/types/streaming.py +201 -0
- agentrun_sdk/types/tools.py +258 -0
- agentrun_sdk/types/traces.py +5 -0
- agentrun_sdk-0.1.2.dist-info/METADATA +51 -0
- agentrun_sdk-0.1.2.dist-info/RECORD +115 -0
- agentrun_sdk-0.1.2.dist-info/WHEEL +5 -0
- agentrun_sdk-0.1.2.dist-info/entry_points.txt +2 -0
- agentrun_sdk-0.1.2.dist-info/top_level.txt +3 -0
- agentrun_wrapper/__init__.py +11 -0
- agentrun_wrapper/_utils/__init__.py +6 -0
- agentrun_wrapper/_utils/endpoints.py +16 -0
- agentrun_wrapper/identity/__init__.py +5 -0
- agentrun_wrapper/identity/auth.py +211 -0
- agentrun_wrapper/memory/__init__.py +6 -0
- agentrun_wrapper/memory/client.py +1697 -0
- agentrun_wrapper/memory/constants.py +103 -0
- agentrun_wrapper/memory/controlplane.py +626 -0
- agentrun_wrapper/py.typed +1 -0
- agentrun_wrapper/runtime/__init__.py +13 -0
- agentrun_wrapper/runtime/app.py +473 -0
- agentrun_wrapper/runtime/context.py +34 -0
- agentrun_wrapper/runtime/models.py +25 -0
- agentrun_wrapper/services/__init__.py +1 -0
- agentrun_wrapper/services/identity.py +192 -0
- agentrun_wrapper/tools/__init__.py +6 -0
- agentrun_wrapper/tools/browser_client.py +325 -0
- agentrun_wrapper/tools/code_interpreter_client.py +186 -0
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
"""AgentCore Memory SDK - Control Plane Client.
|
|
2
|
+
|
|
3
|
+
This module provides a simplified interface for Bedrock AgentCore Memory control plane operations.
|
|
4
|
+
It handles memory resource management, strategy operations, and status monitoring.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import time
|
|
10
|
+
import uuid
|
|
11
|
+
from typing import Any, Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
import boto3
|
|
14
|
+
from botocore.exceptions import ClientError
|
|
15
|
+
|
|
16
|
+
from .constants import (
|
|
17
|
+
MemoryStatus,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MemoryControlPlaneClient:
|
|
24
|
+
"""Client for Bedrock AgentCore Memory control plane operations."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, region_name: str = "us-west-2", environment: str = "prod"):
|
|
27
|
+
"""Initialize the Memory Control Plane client.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
region_name: AWS region name
|
|
31
|
+
environment: Environment name (prod, gamma, etc.)
|
|
32
|
+
"""
|
|
33
|
+
self.region_name = region_name
|
|
34
|
+
self.environment = environment
|
|
35
|
+
|
|
36
|
+
self.endpoint = os.getenv(
|
|
37
|
+
"BEDROCK_AGENTCORE_CONTROL_ENDPOINT", f"https://bedrock-agentcore-control.{region_name}.amazonaws.com"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
service_name = os.getenv("BEDROCK_AGENTCORE_CONTROL_SERVICE", "bedrock-agentcore-control")
|
|
41
|
+
self.client = boto3.client(service_name, region_name=self.region_name, endpoint_url=self.endpoint)
|
|
42
|
+
|
|
43
|
+
logger.info("Initialized MemoryControlPlaneClient for %s in %s", environment, region_name)
|
|
44
|
+
|
|
45
|
+
# ==================== MEMORY OPERATIONS ====================
|
|
46
|
+
|
|
47
|
+
def create_memory(
|
|
48
|
+
self,
|
|
49
|
+
name: str,
|
|
50
|
+
event_expiry_days: int = 90,
|
|
51
|
+
description: Optional[str] = None,
|
|
52
|
+
memory_execution_role_arn: Optional[str] = None,
|
|
53
|
+
strategies: Optional[List[Dict[str, Any]]] = None,
|
|
54
|
+
wait_for_active: bool = False,
|
|
55
|
+
max_wait: int = 300,
|
|
56
|
+
poll_interval: int = 10,
|
|
57
|
+
) -> Dict[str, Any]:
|
|
58
|
+
"""Create a memory resource with optional strategies.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
name: Name for the memory resource
|
|
62
|
+
event_expiry_days: How long to retain events (default: 90 days)
|
|
63
|
+
description: Optional description
|
|
64
|
+
memory_execution_role_arn: IAM role ARN for memory execution
|
|
65
|
+
strategies: Optional list of strategy configurations
|
|
66
|
+
wait_for_active: Whether to wait for memory to become ACTIVE
|
|
67
|
+
max_wait: Maximum seconds to wait if wait_for_active is True
|
|
68
|
+
poll_interval: Seconds between status checks if wait_for_active is True
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Created memory object
|
|
72
|
+
"""
|
|
73
|
+
params = {
|
|
74
|
+
"name": name,
|
|
75
|
+
"eventExpiryDuration": event_expiry_days,
|
|
76
|
+
"clientToken": str(uuid.uuid4()),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if description:
|
|
80
|
+
params["description"] = description
|
|
81
|
+
|
|
82
|
+
if memory_execution_role_arn:
|
|
83
|
+
params["memoryExecutionRoleArn"] = memory_execution_role_arn
|
|
84
|
+
|
|
85
|
+
if strategies:
|
|
86
|
+
params["memoryStrategies"] = strategies
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
response = self.client.create_memory(**params)
|
|
90
|
+
memory = response["memory"]
|
|
91
|
+
memory_id = memory["id"]
|
|
92
|
+
|
|
93
|
+
logger.info("Created memory: %s", memory_id)
|
|
94
|
+
|
|
95
|
+
if wait_for_active:
|
|
96
|
+
return self._wait_for_memory_active(memory_id, max_wait, poll_interval)
|
|
97
|
+
|
|
98
|
+
return memory
|
|
99
|
+
|
|
100
|
+
except ClientError as e:
|
|
101
|
+
logger.error("Failed to create memory: %s", e)
|
|
102
|
+
raise
|
|
103
|
+
|
|
104
|
+
def get_memory(self, memory_id: str, include_strategies: bool = True) -> Dict[str, Any]:
|
|
105
|
+
"""Get a memory resource by ID.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
memory_id: Memory resource ID
|
|
109
|
+
include_strategies: Whether to include strategy details in response
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Memory resource details
|
|
113
|
+
"""
|
|
114
|
+
try:
|
|
115
|
+
response = self.client.get_memory(memoryId=memory_id)
|
|
116
|
+
memory = response["memory"]
|
|
117
|
+
|
|
118
|
+
# Add strategy count
|
|
119
|
+
strategies = memory.get("strategies", [])
|
|
120
|
+
memory["strategyCount"] = len(strategies)
|
|
121
|
+
|
|
122
|
+
# Remove strategies if not requested
|
|
123
|
+
if not include_strategies and "strategies" in memory:
|
|
124
|
+
del memory["strategies"]
|
|
125
|
+
|
|
126
|
+
return memory
|
|
127
|
+
|
|
128
|
+
except ClientError as e:
|
|
129
|
+
logger.error("Failed to get memory: %s", e)
|
|
130
|
+
raise
|
|
131
|
+
|
|
132
|
+
def list_memories(self, max_results: int = 100) -> List[Dict[str, Any]]:
|
|
133
|
+
"""List all memories for the account with pagination support.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
max_results: Maximum number of memories to return
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
List of memory summaries
|
|
140
|
+
"""
|
|
141
|
+
try:
|
|
142
|
+
memories = []
|
|
143
|
+
next_token = None
|
|
144
|
+
|
|
145
|
+
while len(memories) < max_results:
|
|
146
|
+
params = {"maxResults": min(100, max_results - len(memories))}
|
|
147
|
+
if next_token:
|
|
148
|
+
params["nextToken"] = next_token
|
|
149
|
+
|
|
150
|
+
response = self.client.list_memories(**params)
|
|
151
|
+
batch = response.get("memories", [])
|
|
152
|
+
memories.extend(batch)
|
|
153
|
+
|
|
154
|
+
next_token = response.get("nextToken")
|
|
155
|
+
if not next_token or len(memories) >= max_results:
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
# Add strategy count to each memory summary
|
|
159
|
+
for memory in memories:
|
|
160
|
+
memory["strategyCount"] = 0 # List memories doesn't include strategies
|
|
161
|
+
|
|
162
|
+
return memories[:max_results]
|
|
163
|
+
|
|
164
|
+
except ClientError as e:
|
|
165
|
+
logger.error("Failed to list memories: %s", e)
|
|
166
|
+
raise
|
|
167
|
+
|
|
168
|
+
def update_memory(
|
|
169
|
+
self,
|
|
170
|
+
memory_id: str,
|
|
171
|
+
description: Optional[str] = None,
|
|
172
|
+
event_expiry_days: Optional[int] = None,
|
|
173
|
+
memory_execution_role_arn: Optional[str] = None,
|
|
174
|
+
add_strategies: Optional[List[Dict[str, Any]]] = None,
|
|
175
|
+
modify_strategies: Optional[List[Dict[str, Any]]] = None,
|
|
176
|
+
delete_strategy_ids: Optional[List[str]] = None,
|
|
177
|
+
wait_for_active: bool = False,
|
|
178
|
+
max_wait: int = 300,
|
|
179
|
+
poll_interval: int = 10,
|
|
180
|
+
) -> Dict[str, Any]:
|
|
181
|
+
"""Update a memory resource properties and/or strategies.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
memory_id: Memory resource ID
|
|
185
|
+
description: Optional new description
|
|
186
|
+
event_expiry_days: Optional new event expiry duration
|
|
187
|
+
memory_execution_role_arn: Optional new execution role ARN
|
|
188
|
+
add_strategies: Optional list of strategies to add
|
|
189
|
+
modify_strategies: Optional list of strategies to modify
|
|
190
|
+
delete_strategy_ids: Optional list of strategy IDs to delete
|
|
191
|
+
wait_for_active: Whether to wait for memory to become ACTIVE
|
|
192
|
+
max_wait: Maximum seconds to wait if wait_for_active is True
|
|
193
|
+
poll_interval: Seconds between status checks if wait_for_active is True
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Updated memory object
|
|
197
|
+
"""
|
|
198
|
+
params: Dict = {
|
|
199
|
+
"memoryId": memory_id,
|
|
200
|
+
"clientToken": str(uuid.uuid4()),
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
# Add memory properties if provided
|
|
204
|
+
if description is not None:
|
|
205
|
+
params["description"] = description
|
|
206
|
+
|
|
207
|
+
if event_expiry_days is not None:
|
|
208
|
+
params["eventExpiryDuration"] = event_expiry_days
|
|
209
|
+
|
|
210
|
+
if memory_execution_role_arn is not None:
|
|
211
|
+
params["memoryExecutionRoleArn"] = memory_execution_role_arn
|
|
212
|
+
|
|
213
|
+
# Add strategy operations if provided
|
|
214
|
+
memory_strategies = {}
|
|
215
|
+
|
|
216
|
+
if add_strategies:
|
|
217
|
+
memory_strategies["addMemoryStrategies"] = add_strategies
|
|
218
|
+
|
|
219
|
+
if modify_strategies:
|
|
220
|
+
memory_strategies["modifyMemoryStrategies"] = modify_strategies
|
|
221
|
+
|
|
222
|
+
if delete_strategy_ids:
|
|
223
|
+
memory_strategies["deleteMemoryStrategies"] = [
|
|
224
|
+
{"memoryStrategyId": strategy_id} for strategy_id in delete_strategy_ids
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
if memory_strategies:
|
|
228
|
+
params["memoryStrategies"] = memory_strategies
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
response = self.client.update_memory(**params)
|
|
232
|
+
memory = response["memory"]
|
|
233
|
+
logger.info("Updated memory: %s", memory_id)
|
|
234
|
+
|
|
235
|
+
if wait_for_active:
|
|
236
|
+
return self._wait_for_memory_active(memory_id, max_wait, poll_interval)
|
|
237
|
+
|
|
238
|
+
return memory
|
|
239
|
+
|
|
240
|
+
except ClientError as e:
|
|
241
|
+
logger.error("Failed to update memory: %s", e)
|
|
242
|
+
raise
|
|
243
|
+
|
|
244
|
+
def delete_memory(
|
|
245
|
+
self,
|
|
246
|
+
memory_id: str,
|
|
247
|
+
wait_for_deletion: bool = False,
|
|
248
|
+
wait_for_strategies: bool = False, # Changed default to False
|
|
249
|
+
max_wait: int = 300,
|
|
250
|
+
poll_interval: int = 10,
|
|
251
|
+
) -> Dict[str, Any]:
|
|
252
|
+
"""Delete a memory resource.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
memory_id: Memory resource ID to delete
|
|
256
|
+
wait_for_deletion: Whether to wait for complete deletion
|
|
257
|
+
wait_for_strategies: Whether to wait for strategies to become ACTIVE before deletion
|
|
258
|
+
max_wait: Maximum seconds to wait if wait_for_deletion is True
|
|
259
|
+
poll_interval: Seconds between checks if wait_for_deletion is True
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Deletion response
|
|
263
|
+
"""
|
|
264
|
+
try:
|
|
265
|
+
# If requested, wait for all strategies to become ACTIVE before deletion
|
|
266
|
+
if wait_for_strategies:
|
|
267
|
+
try:
|
|
268
|
+
memory = self.get_memory(memory_id)
|
|
269
|
+
strategies = memory.get("strategies", [])
|
|
270
|
+
|
|
271
|
+
# Check if any strategies are in a transitional state
|
|
272
|
+
transitional_strategies = [
|
|
273
|
+
s
|
|
274
|
+
for s in strategies
|
|
275
|
+
if s.get("status") not in [MemoryStatus.ACTIVE.value, MemoryStatus.FAILED.value]
|
|
276
|
+
]
|
|
277
|
+
|
|
278
|
+
if transitional_strategies:
|
|
279
|
+
logger.info(
|
|
280
|
+
"Waiting for %d strategies to become ACTIVE before deletion", len(transitional_strategies)
|
|
281
|
+
)
|
|
282
|
+
self._wait_for_status(
|
|
283
|
+
memory_id=memory_id,
|
|
284
|
+
target_status=MemoryStatus.ACTIVE.value,
|
|
285
|
+
max_wait=max_wait,
|
|
286
|
+
poll_interval=poll_interval,
|
|
287
|
+
check_strategies=True,
|
|
288
|
+
)
|
|
289
|
+
except Exception as e:
|
|
290
|
+
logger.warning("Error waiting for strategies to become ACTIVE: %s", e)
|
|
291
|
+
|
|
292
|
+
# Now delete the memory
|
|
293
|
+
response = self.client.delete_memory(memoryId=memory_id, clientToken=str(uuid.uuid4()))
|
|
294
|
+
|
|
295
|
+
logger.info("Initiated deletion of memory: %s", memory_id)
|
|
296
|
+
|
|
297
|
+
if not wait_for_deletion:
|
|
298
|
+
return response
|
|
299
|
+
|
|
300
|
+
# Wait for deletion to complete
|
|
301
|
+
start_time = time.time()
|
|
302
|
+
while time.time() - start_time < max_wait:
|
|
303
|
+
try:
|
|
304
|
+
self.client.get_memory(memoryId=memory_id)
|
|
305
|
+
time.sleep(poll_interval)
|
|
306
|
+
except ClientError as e:
|
|
307
|
+
if e.response["Error"]["Code"] == "ResourceNotFoundException":
|
|
308
|
+
logger.info("Memory %s successfully deleted", memory_id)
|
|
309
|
+
return response
|
|
310
|
+
raise
|
|
311
|
+
|
|
312
|
+
raise TimeoutError(f"Memory {memory_id} was not deleted within {max_wait} seconds")
|
|
313
|
+
|
|
314
|
+
except ClientError as e:
|
|
315
|
+
logger.error("Failed to delete memory: %s", e)
|
|
316
|
+
raise
|
|
317
|
+
|
|
318
|
+
# ==================== STRATEGY OPERATIONS ====================
|
|
319
|
+
|
|
320
|
+
def add_strategy(
|
|
321
|
+
self,
|
|
322
|
+
memory_id: str,
|
|
323
|
+
strategy: Dict[str, Any],
|
|
324
|
+
wait_for_active: bool = False,
|
|
325
|
+
max_wait: int = 300,
|
|
326
|
+
poll_interval: int = 10,
|
|
327
|
+
) -> Dict[str, Any]:
|
|
328
|
+
"""Add a strategy to a memory resource.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
memory_id: Memory resource ID
|
|
332
|
+
strategy: Strategy configuration dictionary
|
|
333
|
+
wait_for_active: Whether to wait for strategy to become ACTIVE
|
|
334
|
+
max_wait: Maximum seconds to wait if wait_for_active is True
|
|
335
|
+
poll_interval: Seconds between status checks if wait_for_active is True
|
|
336
|
+
|
|
337
|
+
Returns:
|
|
338
|
+
Updated memory object with strategyId field
|
|
339
|
+
"""
|
|
340
|
+
# Get the strategy type and name for identification
|
|
341
|
+
strategy_type = list(strategy.keys())[0] # e.g., 'semanticMemoryStrategy'
|
|
342
|
+
strategy_name = strategy[strategy_type].get("name")
|
|
343
|
+
|
|
344
|
+
logger.info("Adding strategy %s of type %s to memory %s", strategy_name, strategy_type, memory_id)
|
|
345
|
+
|
|
346
|
+
# Use update_memory with add_strategies parameter but don't wait for memory
|
|
347
|
+
memory = self.update_memory(
|
|
348
|
+
memory_id=memory_id,
|
|
349
|
+
add_strategies=[strategy],
|
|
350
|
+
wait_for_active=False, # Don't wait for memory, we'll check strategy specifically
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# If we need to wait for the strategy to become active
|
|
354
|
+
if wait_for_active:
|
|
355
|
+
# First, get the memory again to ensure we have the latest state
|
|
356
|
+
memory = self.get_memory(memory_id)
|
|
357
|
+
|
|
358
|
+
# Find the newly added strategy by matching name
|
|
359
|
+
strategies = memory.get("strategies", [])
|
|
360
|
+
strategy_id = None
|
|
361
|
+
|
|
362
|
+
for s in strategies:
|
|
363
|
+
# Match by name since that's unique within a memory
|
|
364
|
+
if s.get("name") == strategy_name:
|
|
365
|
+
strategy_id = s.get("strategyId")
|
|
366
|
+
logger.info("Found newly added strategy %s with ID %s", strategy_name, strategy_id)
|
|
367
|
+
break
|
|
368
|
+
|
|
369
|
+
if strategy_id:
|
|
370
|
+
return self._wait_for_strategy_active(memory_id, strategy_id, max_wait, poll_interval)
|
|
371
|
+
else:
|
|
372
|
+
logger.warning("Could not identify newly added strategy %s to wait for activation", strategy_name)
|
|
373
|
+
|
|
374
|
+
return memory
|
|
375
|
+
|
|
376
|
+
def get_strategy(self, memory_id: str, strategy_id: str) -> Dict[str, Any]:
|
|
377
|
+
"""Get a specific strategy from a memory resource.
|
|
378
|
+
|
|
379
|
+
Args:
|
|
380
|
+
memory_id: Memory resource ID
|
|
381
|
+
strategy_id: Strategy ID
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
Strategy details
|
|
385
|
+
"""
|
|
386
|
+
try:
|
|
387
|
+
memory = self.get_memory(memory_id)
|
|
388
|
+
strategies = memory.get("strategies", [])
|
|
389
|
+
|
|
390
|
+
for strategy in strategies:
|
|
391
|
+
if strategy.get("strategyId") == strategy_id:
|
|
392
|
+
return strategy
|
|
393
|
+
|
|
394
|
+
raise ValueError(f"Strategy {strategy_id} not found in memory {memory_id}")
|
|
395
|
+
|
|
396
|
+
except ClientError as e:
|
|
397
|
+
logger.error("Failed to get strategy: %s", e)
|
|
398
|
+
raise
|
|
399
|
+
|
|
400
|
+
def update_strategy(
|
|
401
|
+
self,
|
|
402
|
+
memory_id: str,
|
|
403
|
+
strategy_id: str,
|
|
404
|
+
description: Optional[str] = None,
|
|
405
|
+
namespaces: Optional[List[str]] = None,
|
|
406
|
+
configuration: Optional[Dict[str, Any]] = None,
|
|
407
|
+
wait_for_active: bool = False,
|
|
408
|
+
max_wait: int = 300,
|
|
409
|
+
poll_interval: int = 10,
|
|
410
|
+
) -> Dict[str, Any]:
|
|
411
|
+
"""Update a strategy in a memory resource.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
memory_id: Memory resource ID
|
|
415
|
+
strategy_id: Strategy ID to update
|
|
416
|
+
description: Optional new description
|
|
417
|
+
namespaces: Optional new namespaces list
|
|
418
|
+
configuration: Optional new configuration
|
|
419
|
+
wait_for_active: Whether to wait for strategy to become ACTIVE
|
|
420
|
+
max_wait: Maximum seconds to wait if wait_for_active is True
|
|
421
|
+
poll_interval: Seconds between status checks if wait_for_active is True
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
Updated memory object
|
|
425
|
+
"""
|
|
426
|
+
# Note: API expects memoryStrategyId for input but returns strategyId in response
|
|
427
|
+
modify_config: Dict = {"memoryStrategyId": strategy_id}
|
|
428
|
+
|
|
429
|
+
if description is not None:
|
|
430
|
+
modify_config["description"] = description
|
|
431
|
+
|
|
432
|
+
if namespaces is not None:
|
|
433
|
+
modify_config["namespaces"] = namespaces
|
|
434
|
+
|
|
435
|
+
if configuration is not None:
|
|
436
|
+
modify_config["configuration"] = configuration
|
|
437
|
+
|
|
438
|
+
# Use update_memory with modify_strategies parameter but don't wait for memory
|
|
439
|
+
memory = self.update_memory(
|
|
440
|
+
memory_id=memory_id,
|
|
441
|
+
modify_strategies=[modify_config],
|
|
442
|
+
wait_for_active=False, # Don't wait for memory, we'll check strategy specifically
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
# If we need to wait for the strategy to become active
|
|
446
|
+
if wait_for_active:
|
|
447
|
+
return self._wait_for_strategy_active(memory_id, strategy_id, max_wait, poll_interval)
|
|
448
|
+
|
|
449
|
+
return memory
|
|
450
|
+
|
|
451
|
+
def remove_strategy(
|
|
452
|
+
self,
|
|
453
|
+
memory_id: str,
|
|
454
|
+
strategy_id: str,
|
|
455
|
+
wait_for_active: bool = False,
|
|
456
|
+
max_wait: int = 300,
|
|
457
|
+
poll_interval: int = 10,
|
|
458
|
+
) -> Dict[str, Any]:
|
|
459
|
+
"""Remove a strategy from a memory resource.
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
memory_id: Memory resource ID
|
|
463
|
+
strategy_id: Strategy ID to remove
|
|
464
|
+
wait_for_active: Whether to wait for memory to become ACTIVE
|
|
465
|
+
max_wait: Maximum seconds to wait if wait_for_active is True
|
|
466
|
+
poll_interval: Seconds between status checks if wait_for_active is True
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
Updated memory object
|
|
470
|
+
"""
|
|
471
|
+
# For remove_strategy, we only need to wait for memory to be active
|
|
472
|
+
# since the strategy will be gone
|
|
473
|
+
return self.update_memory(
|
|
474
|
+
memory_id=memory_id,
|
|
475
|
+
delete_strategy_ids=[strategy_id],
|
|
476
|
+
wait_for_active=wait_for_active,
|
|
477
|
+
max_wait=max_wait,
|
|
478
|
+
poll_interval=poll_interval,
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
# ==================== HELPER METHODS ====================
|
|
482
|
+
|
|
483
|
+
def _wait_for_memory_active(self, memory_id: str, max_wait: int, poll_interval: int) -> Dict[str, Any]:
|
|
484
|
+
"""Wait for memory to return to ACTIVE state."""
|
|
485
|
+
logger.info("Waiting for memory %s to become ACTIVE...", memory_id)
|
|
486
|
+
return self._wait_for_status(
|
|
487
|
+
memory_id=memory_id, target_status=MemoryStatus.ACTIVE.value, max_wait=max_wait, poll_interval=poll_interval
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
def _wait_for_strategy_active(
|
|
491
|
+
self, memory_id: str, strategy_id: str, max_wait: int, poll_interval: int
|
|
492
|
+
) -> Dict[str, Any]:
|
|
493
|
+
"""Wait for specific memory strategy to become ACTIVE."""
|
|
494
|
+
logger.info("Waiting for strategy %s to become ACTIVE (max wait: %d seconds)...", strategy_id, max_wait)
|
|
495
|
+
|
|
496
|
+
start_time = time.time()
|
|
497
|
+
last_status = None
|
|
498
|
+
|
|
499
|
+
while time.time() - start_time < max_wait:
|
|
500
|
+
try:
|
|
501
|
+
memory = self.get_memory(memory_id)
|
|
502
|
+
strategies = memory.get("strategies", [])
|
|
503
|
+
|
|
504
|
+
for strategy in strategies:
|
|
505
|
+
if strategy.get("strategyId") == strategy_id:
|
|
506
|
+
status = strategy["status"]
|
|
507
|
+
|
|
508
|
+
# Log status changes
|
|
509
|
+
if status != last_status:
|
|
510
|
+
logger.info("Strategy %s status: %s", strategy_id, status)
|
|
511
|
+
last_status = status
|
|
512
|
+
|
|
513
|
+
if status == MemoryStatus.ACTIVE.value:
|
|
514
|
+
elapsed = time.time() - start_time
|
|
515
|
+
logger.info("Strategy %s is now ACTIVE (took %.1f seconds)", strategy_id, elapsed)
|
|
516
|
+
return memory
|
|
517
|
+
elif status == MemoryStatus.FAILED.value:
|
|
518
|
+
failure_reason = strategy.get("failureReason", "Unknown")
|
|
519
|
+
raise RuntimeError(f"Strategy {strategy_id} failed to activate: {failure_reason}")
|
|
520
|
+
|
|
521
|
+
break
|
|
522
|
+
else:
|
|
523
|
+
logger.warning("Strategy %s not found in memory %s", strategy_id, memory_id)
|
|
524
|
+
|
|
525
|
+
# Wait before checking again
|
|
526
|
+
time.sleep(poll_interval)
|
|
527
|
+
|
|
528
|
+
except ClientError as e:
|
|
529
|
+
logger.error("Error checking strategy status: %s", e)
|
|
530
|
+
raise
|
|
531
|
+
|
|
532
|
+
elapsed = time.time() - start_time
|
|
533
|
+
raise TimeoutError(
|
|
534
|
+
f"Strategy {strategy_id} did not become ACTIVE within {max_wait} seconds (last status: {last_status})"
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
def _wait_for_status(
|
|
538
|
+
self, memory_id: str, target_status: str, max_wait: int, poll_interval: int, check_strategies: bool = True
|
|
539
|
+
) -> Dict[str, Any]:
|
|
540
|
+
"""Generic method to wait for a memory to reach a specific status.
|
|
541
|
+
|
|
542
|
+
Args:
|
|
543
|
+
memory_id: The ID of the memory to check
|
|
544
|
+
target_status: The status to wait for (e.g., "ACTIVE")
|
|
545
|
+
max_wait: Maximum time to wait in seconds
|
|
546
|
+
poll_interval: Time between status checks in seconds
|
|
547
|
+
check_strategies: Whether to also check that all strategies are in the target status
|
|
548
|
+
|
|
549
|
+
Returns:
|
|
550
|
+
The memory object once it reaches the target status
|
|
551
|
+
|
|
552
|
+
Raises:
|
|
553
|
+
TimeoutError: If the memory doesn't reach the target status within max_wait
|
|
554
|
+
RuntimeError: If the memory or any strategy reaches a FAILED state
|
|
555
|
+
"""
|
|
556
|
+
logger.info("Waiting for memory %s to reach status %s...", memory_id, target_status)
|
|
557
|
+
|
|
558
|
+
start_time = time.time()
|
|
559
|
+
last_memory_status = None
|
|
560
|
+
strategy_statuses = {}
|
|
561
|
+
|
|
562
|
+
while time.time() - start_time < max_wait:
|
|
563
|
+
try:
|
|
564
|
+
memory = self.get_memory(memory_id)
|
|
565
|
+
status = memory.get("status")
|
|
566
|
+
|
|
567
|
+
# Log status changes for memory
|
|
568
|
+
if status != last_memory_status:
|
|
569
|
+
logger.info("Memory %s status: %s", memory_id, status)
|
|
570
|
+
last_memory_status = status
|
|
571
|
+
|
|
572
|
+
if status == target_status:
|
|
573
|
+
# Check if all strategies are also in the target status
|
|
574
|
+
if check_strategies and target_status == MemoryStatus.ACTIVE.value:
|
|
575
|
+
strategies = memory.get("strategies", [])
|
|
576
|
+
all_strategies_active = True
|
|
577
|
+
|
|
578
|
+
for strategy in strategies:
|
|
579
|
+
strategy_id = strategy.get("strategyId")
|
|
580
|
+
strategy_status = strategy.get("status")
|
|
581
|
+
|
|
582
|
+
# Log strategy status changes
|
|
583
|
+
if (
|
|
584
|
+
strategy_id not in strategy_statuses
|
|
585
|
+
or strategy_statuses[strategy_id] != strategy_status
|
|
586
|
+
):
|
|
587
|
+
logger.info("Strategy %s status: %s", strategy_id, strategy_status)
|
|
588
|
+
strategy_statuses[strategy_id] = strategy_status
|
|
589
|
+
|
|
590
|
+
if strategy_status != target_status:
|
|
591
|
+
if strategy_status == MemoryStatus.FAILED.value:
|
|
592
|
+
failure_reason = strategy.get("failureReason", "Unknown")
|
|
593
|
+
raise RuntimeError(f"Strategy {strategy_id} failed: {failure_reason}")
|
|
594
|
+
|
|
595
|
+
all_strategies_active = False
|
|
596
|
+
|
|
597
|
+
if not all_strategies_active:
|
|
598
|
+
logger.info(
|
|
599
|
+
"Memory %s is %s but %d strategies are still processing",
|
|
600
|
+
memory_id,
|
|
601
|
+
target_status,
|
|
602
|
+
len([s for s in strategies if s.get("status") != target_status]),
|
|
603
|
+
)
|
|
604
|
+
time.sleep(poll_interval)
|
|
605
|
+
continue
|
|
606
|
+
|
|
607
|
+
elapsed = time.time() - start_time
|
|
608
|
+
logger.info(
|
|
609
|
+
"Memory %s and all strategies are now %s (took %.1f seconds)", memory_id, target_status, elapsed
|
|
610
|
+
)
|
|
611
|
+
return memory
|
|
612
|
+
elif status == MemoryStatus.FAILED.value:
|
|
613
|
+
failure_reason = memory.get("failureReason", "Unknown")
|
|
614
|
+
raise RuntimeError(f"Memory operation failed: {failure_reason}")
|
|
615
|
+
|
|
616
|
+
time.sleep(poll_interval)
|
|
617
|
+
|
|
618
|
+
except ClientError as e:
|
|
619
|
+
logger.error("Error checking memory status: %s", e)
|
|
620
|
+
raise
|
|
621
|
+
|
|
622
|
+
elapsed = time.time() - start_time
|
|
623
|
+
raise TimeoutError(
|
|
624
|
+
f"Memory {memory_id} did not reach status {target_status} within {max_wait} seconds "
|
|
625
|
+
f"(elapsed: {elapsed:.1f}s)"
|
|
626
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Marker file that indicates this package supports typing
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""BedrockAgentCore Runtime Package.
|
|
2
|
+
|
|
3
|
+
This package contains the core runtime components for Bedrock AgentCore applications:
|
|
4
|
+
- BedrockAgentCoreApp: Main application class
|
|
5
|
+
- RequestContext: HTTP request context
|
|
6
|
+
- BedrockAgentCoreContext: Agent identity context
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .app import BedrockAgentCoreApp
|
|
10
|
+
from .context import BedrockAgentCoreContext, RequestContext
|
|
11
|
+
from .models import PingStatus
|
|
12
|
+
|
|
13
|
+
__all__ = ["BedrockAgentCoreApp", "RequestContext", "BedrockAgentCoreContext", "PingStatus"]
|