bedrock-agentcore-starter-toolkit 0.1.11__py3-none-any.whl → 0.1.13__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.

Files changed (25) hide show
  1. bedrock_agentcore_starter_toolkit/cli/import_agent/agent_info.py +6 -1
  2. bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +101 -4
  3. bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py +68 -1
  4. bedrock_agentcore_starter_toolkit/operations/gateway/client.py +139 -0
  5. bedrock_agentcore_starter_toolkit/operations/memory/__init__.py +1 -0
  6. bedrock_agentcore_starter_toolkit/operations/memory/constants.py +98 -0
  7. bedrock_agentcore_starter_toolkit/operations/memory/manager.py +890 -0
  8. bedrock_agentcore_starter_toolkit/operations/memory/models/DictWrapper.py +51 -0
  9. bedrock_agentcore_starter_toolkit/operations/memory/models/Memory.py +17 -0
  10. bedrock_agentcore_starter_toolkit/operations/memory/models/MemoryStrategy.py +17 -0
  11. bedrock_agentcore_starter_toolkit/operations/memory/models/MemorySummary.py +17 -0
  12. bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +4 -0
  13. bedrock_agentcore_starter_toolkit/operations/runtime/create_role.py +3 -2
  14. bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py +8 -2
  15. bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +1 -0
  16. bedrock_agentcore_starter_toolkit/services/codebuild.py +17 -6
  17. bedrock_agentcore_starter_toolkit/services/runtime.py +71 -5
  18. bedrock_agentcore_starter_toolkit/utils/runtime/schema.py +1 -0
  19. bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2 +25 -11
  20. {bedrock_agentcore_starter_toolkit-0.1.11.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/METADATA +19 -4
  21. {bedrock_agentcore_starter_toolkit-0.1.11.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/RECORD +25 -18
  22. {bedrock_agentcore_starter_toolkit-0.1.11.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/WHEEL +0 -0
  23. {bedrock_agentcore_starter_toolkit-0.1.11.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/entry_points.txt +0 -0
  24. {bedrock_agentcore_starter_toolkit-0.1.11.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/licenses/LICENSE.txt +0 -0
  25. {bedrock_agentcore_starter_toolkit-0.1.11.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/licenses/NOTICE.txt +0 -0
@@ -148,7 +148,12 @@ def get_agent_info(agent_id: str, agent_alias_id: str, bedrock_client, bedrock_a
148
148
  s3_object_key = action_group["apiSchema"]["s3"]["s3ObjectKey"]
149
149
 
150
150
  s3_client = boto3.client("s3")
151
- response = s3_client.get_object(Bucket=s3_bucket_name, Key=s3_object_key)
151
+ # Get account ID for bucket ownership verification
152
+ sts_client = boto3.client("sts")
153
+ account_id = sts_client.get_caller_identity()["Account"]
154
+ response = s3_client.get_object(
155
+ Bucket=s3_bucket_name, Key=s3_object_key, ExpectedBucketOwner=account_id
156
+ )
152
157
  yaml_content = response["Body"].read().decode("utf-8")
153
158
  yaml = YAML(typ="safe")
154
159
  action_group["apiSchema"]["payload"] = yaml.load(yaml_content)
@@ -68,7 +68,7 @@ def _prompt_for_requirements_file(prompt_text: str, default: str = "") -> Option
68
68
  return None
69
69
 
70
70
 
71
- def _handle_requirements_file_display(requirements_file: Optional[str]) -> Optional[str]:
71
+ def _handle_requirements_file_display(requirements_file: Optional[str], non_interactive: bool = False) -> Optional[str]:
72
72
  """Handle requirements file with display logic for CLI."""
73
73
  from ...utils.runtime.entrypoint import detect_dependencies
74
74
 
@@ -76,6 +76,15 @@ def _handle_requirements_file_display(requirements_file: Optional[str]) -> Optio
76
76
  # User provided file - validate and show confirmation
77
77
  return _validate_requirements_file(requirements_file)
78
78
 
79
+ if non_interactive:
80
+ # Auto-detection for non-interactive mode
81
+ deps = detect_dependencies(Path.cwd())
82
+ if deps.found:
83
+ _print_success(f"Using detected file: [dim]{deps.file}[/dim]")
84
+ return None # Use detected file
85
+ else:
86
+ _handle_error("No requirements file specified and none found automatically")
87
+
79
88
  # Auto-detection with interactive prompt
80
89
  deps = detect_dependencies(Path.cwd())
81
90
 
@@ -167,9 +176,19 @@ def configure(
167
176
  authorizer_config: Optional[str] = typer.Option(
168
177
  None, "--authorizer-config", "-ac", help="OAuth authorizer configuration as JSON string"
169
178
  ),
179
+ request_header_allowlist: Optional[str] = typer.Option(
180
+ None,
181
+ "--request-header-allowlist",
182
+ "-rha",
183
+ help="Comma-separated list of allowed request headers "
184
+ "(Authorization or X-Amzn-Bedrock-AgentCore-Runtime-Custom-*)",
185
+ ),
170
186
  verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output"),
171
187
  region: Optional[str] = typer.Option(None, "--region", "-r"),
172
188
  protocol: Optional[str] = typer.Option(None, "--protocol", "-p", help="Server protocol (HTTP or MCP)"),
189
+ non_interactive: bool = typer.Option(
190
+ False, "--non-interactive", "-ni", help="Skip prompts; use defaults unless overridden"
191
+ ),
173
192
  ):
174
193
  """Configure a Bedrock AgentCore agent. The agent name defaults to your Python file name."""
175
194
  if ctx.invoked_subcommand is not None:
@@ -196,7 +215,7 @@ def configure(
196
215
 
197
216
  # Create configuration manager for clean, elegant prompting
198
217
  config_path = Path.cwd() / ".bedrock_agentcore.yaml"
199
- config_manager = ConfigurationManager(config_path)
218
+ config_manager = ConfigurationManager(config_path, non_interactive)
200
219
 
201
220
  # Interactive prompts for missing values - clean and elegant
202
221
  if not execution_role:
@@ -217,7 +236,7 @@ def configure(
217
236
  _print_success(f"Using existing ECR repository: [dim]{ecr_repository}[/dim]")
218
237
 
219
238
  # Handle dependency file selection with simplified logic
220
- final_requirements_file = _handle_requirements_file_display(requirements_file)
239
+ final_requirements_file = _handle_requirements_file_display(requirements_file, non_interactive)
221
240
 
222
241
  # Handle OAuth authorization configuration
223
242
  oauth_config = None
@@ -231,6 +250,19 @@ def configure(
231
250
  else:
232
251
  oauth_config = config_manager.prompt_oauth_config()
233
252
 
253
+ # Handle request header allowlist configuration
254
+ request_header_config = None
255
+ if request_header_allowlist:
256
+ # Parse comma-separated headers and create configuration
257
+ headers = [header.strip() for header in request_header_allowlist.split(",") if header.strip()]
258
+ if headers:
259
+ request_header_config = {"requestHeaderAllowlist": headers}
260
+ _print_success(f"Configured request header allowlist with {len(headers)} headers")
261
+ else:
262
+ _handle_error("Empty request header allowlist provided")
263
+ else:
264
+ request_header_config = config_manager.prompt_request_header_allowlist()
265
+
234
266
  try:
235
267
  result = configure_bedrock_agentcore(
236
268
  agent_name=agent_name,
@@ -242,6 +274,7 @@ def configure(
242
274
  enable_observability=not disable_otel,
243
275
  requirements_file=final_requirements_file,
244
276
  authorizer_configuration=oauth_config,
277
+ request_header_configuration=request_header_config,
245
278
  verbose=verbose,
246
279
  region=region,
247
280
  protocol=protocol.upper() if protocol else None,
@@ -252,6 +285,12 @@ def configure(
252
285
  if oauth_config:
253
286
  auth_info = "OAuth (customJWTAuthorizer)"
254
287
 
288
+ # Prepare request headers info for summary
289
+ headers_info = ""
290
+ if request_header_config:
291
+ headers = request_header_config.get("requestHeaderAllowlist", [])
292
+ headers_info = f"Request Headers Allowlist: [dim]{len(headers)} headers configured[/dim]\n"
293
+
255
294
  console.print(
256
295
  Panel(
257
296
  f"[green]Configuration Complete[/green]\n\n"
@@ -265,7 +304,8 @@ def configure(
265
304
  f"ECR Repository: [dim]"
266
305
  f"{'Auto-create' if result.auto_create_ecr else result.ecr_repository or 'N/A'}"
267
306
  f"[/dim]\n"
268
- f"Authorization: [dim]{auth_info}[/dim]\n\n"
307
+ f"Authorization: [dim]{auth_info}[/dim]\n"
308
+ f"{headers_info}\n"
269
309
  f"📄 Config saved to: [dim]{result.config_path}[/dim]\n\n"
270
310
  f"[bold]Next Steps:[/bold]\n"
271
311
  f" [cyan]agentcore launch[/cyan]",
@@ -570,6 +610,45 @@ def _show_error_response(error_msg: str):
570
610
  console.print(f"\n[red]{error_msg}[/red]")
571
611
 
572
612
 
613
+ def _parse_custom_headers(headers_str: str) -> dict:
614
+ """Parse custom headers string and apply prefix logic.
615
+
616
+ Args:
617
+ headers_str: String in format "Header1:value,Header2:value2"
618
+
619
+ Returns:
620
+ dict: Dictionary of processed headers with proper prefixes
621
+
622
+ Raises:
623
+ ValueError: If header format is invalid
624
+ """
625
+ if not headers_str or not headers_str.strip():
626
+ return {}
627
+
628
+ headers = {}
629
+ header_pairs = [pair.strip() for pair in headers_str.split(",")]
630
+
631
+ for pair in header_pairs:
632
+ if ":" not in pair:
633
+ raise ValueError(f"Invalid header format: '{pair}'. Expected format: 'Header:value'")
634
+
635
+ header_name, header_value = pair.split(":", 1)
636
+ header_name = header_name.strip()
637
+ header_value = header_value.strip()
638
+
639
+ if not header_name:
640
+ raise ValueError(f"Empty header name in: '{pair}'")
641
+
642
+ # Apply prefix logic: if header doesn't start with the custom prefix, add it
643
+ prefix = "X-Amzn-Bedrock-AgentCore-Runtime-Custom-"
644
+ if not header_name.startswith(prefix):
645
+ header_name = prefix + header_name
646
+
647
+ headers[header_name] = header_value
648
+
649
+ return headers
650
+
651
+
573
652
  def invoke(
574
653
  payload: str = typer.Argument(..., help="JSON payload to send"),
575
654
  agent: Optional[str] = typer.Option(
@@ -581,6 +660,12 @@ def invoke(
581
660
  ),
582
661
  local_mode: Optional[bool] = typer.Option(False, "--local", "-l", help="Send request to a running local container"),
583
662
  user_id: Optional[str] = typer.Option(None, "--user-id", "-u", help="User id for authorization flows"),
663
+ headers: Optional[str] = typer.Option(
664
+ None,
665
+ "--headers",
666
+ help="Custom headers (format: 'Header1:value,Header2:value2'). "
667
+ "Headers will be auto-prefixed with 'X-Amzn-Bedrock-AgentCore-Runtime-Custom-' if not already present.",
668
+ ),
584
669
  ):
585
670
  """Invoke Bedrock AgentCore endpoint."""
586
671
  config_path = Path.cwd() / ".bedrock_agentcore.yaml"
@@ -613,6 +698,17 @@ def invoke(
613
698
  "[yellow]Warning: Bearer token provided but OAuth is not configured in .bedrock_agentcore.yaml[/yellow]"
614
699
  )
615
700
 
701
+ # Process custom headers
702
+ custom_headers = {}
703
+ if headers:
704
+ try:
705
+ custom_headers = _parse_custom_headers(headers)
706
+ if custom_headers:
707
+ header_names = list(custom_headers.keys())
708
+ console.print(f"[dim]Using custom headers: {', '.join(header_names)}[/dim]")
709
+ except ValueError as e:
710
+ _handle_error(f"Invalid headers format: {e}")
711
+
616
712
  # Invoke
617
713
  result = invoke_bedrock_agentcore(
618
714
  config_path=config_path,
@@ -622,6 +718,7 @@ def invoke(
622
718
  bearer_token=final_bearer_token,
623
719
  user_id=user_id,
624
720
  local_mode=local_mode,
721
+ custom_headers=custom_headers,
625
722
  )
626
723
  agent_display = config.name if config else (agent or "unknown")
627
724
  _show_invoke_info_panel(agent_display, result, config)
@@ -10,19 +10,25 @@ from ..common import _handle_error, _print_success, _prompt_with_default, consol
10
10
  class ConfigurationManager:
11
11
  """Manages interactive configuration prompts with existing configuration defaults."""
12
12
 
13
- def __init__(self, config_path: Path):
13
+ def __init__(self, config_path: Path, non_interactive: bool = False):
14
14
  """Initialize the ConfigPrompt with a configuration path.
15
15
 
16
16
  Args:
17
17
  config_path: Path to the configuration file
18
+ non_interactive: If True, use defaults without prompting
18
19
  """
19
20
  from ...utils.runtime.config import load_config_if_exists
20
21
 
21
22
  project_config = load_config_if_exists(config_path)
22
23
  self.existing_config = project_config.get_agent_config() if project_config else None
24
+ self.non_interactive = non_interactive
23
25
 
24
26
  def prompt_execution_role(self) -> Optional[str]:
25
27
  """Prompt for execution role. Returns role name/ARN or None for auto-creation."""
28
+ if self.non_interactive:
29
+ _print_success("Will auto-create execution role")
30
+ return None
31
+
26
32
  console.print("\n🔐 [cyan]Execution Role[/cyan]")
27
33
  console.print(
28
34
  "[dim]Press Enter to auto-create execution role, or provide execution role ARN/name to use existing[/dim]"
@@ -43,6 +49,10 @@ class ConfigurationManager:
43
49
 
44
50
  def prompt_ecr_repository(self) -> tuple[Optional[str], bool]:
45
51
  """Prompt for ECR repository. Returns (repository, auto_create_flag)."""
52
+ if self.non_interactive:
53
+ _print_success("Will auto-create ECR repository")
54
+ return None, True
55
+
46
56
  console.print("\n🏗️ [cyan]ECR Repository[/cyan]")
47
57
  console.print(
48
58
  "[dim]Press Enter to auto-create ECR repository, or provide ECR Repository URI to use existing[/dim]"
@@ -63,6 +73,10 @@ class ConfigurationManager:
63
73
 
64
74
  def prompt_oauth_config(self) -> Optional[dict]:
65
75
  """Prompt for OAuth configuration. Returns OAuth config dict or None."""
76
+ if self.non_interactive:
77
+ _print_success("Using default IAM authorization")
78
+ return None
79
+
66
80
  console.print("\n🔐 [cyan]Authorization Configuration[/cyan]")
67
81
  console.print("[dim]By default, Bedrock AgentCore uses IAM authorization.[/dim]")
68
82
 
@@ -131,3 +145,56 @@ class ConfigurationManager:
131
145
 
132
146
  _print_success("OAuth authorizer configuration created")
133
147
  return config
148
+
149
+ def prompt_request_header_allowlist(self) -> Optional[dict]:
150
+ """Prompt for request header allowlist configuration. Returns allowlist config dict or None."""
151
+ if self.non_interactive:
152
+ _print_success("Using default request header configuration")
153
+ return None
154
+
155
+ console.print("\n🔒 [cyan]Request Header Allowlist[/cyan]")
156
+ console.print("[dim]Configure which request headers are allowed to pass through to your agent.[/dim]")
157
+ console.print("[dim]Common headers: Authorization, X-Amzn-Bedrock-AgentCore-Runtime-Custom-*[/dim]")
158
+
159
+ # Get existing allowlist values
160
+ existing_headers = ""
161
+ if (
162
+ self.existing_config
163
+ and self.existing_config.request_header_configuration
164
+ and "requestHeaderAllowlist" in self.existing_config.request_header_configuration
165
+ ):
166
+ existing_headers = ",".join(self.existing_config.request_header_configuration["requestHeaderAllowlist"])
167
+
168
+ allowlist_default = "yes" if existing_headers else "no"
169
+ response = _prompt_with_default("Configure request header allowlist? (yes/no)", allowlist_default)
170
+
171
+ if response.lower() in ["yes", "y"]:
172
+ return self._configure_request_header_allowlist(existing_headers)
173
+ else:
174
+ _print_success("Using default request header configuration")
175
+ return None
176
+
177
+ def _configure_request_header_allowlist(self, existing_headers: str = "") -> dict:
178
+ """Configure request header allowlist and return config dict."""
179
+ console.print("\n📋 [cyan]Request Header Allowlist Configuration[/cyan]")
180
+
181
+ # Show existing config if available
182
+ if existing_headers:
183
+ console.print(f"[dim]Previously configured: {existing_headers}[/dim]")
184
+
185
+ # Prompt for headers
186
+ default_headers = existing_headers or "Authorization,X-Amzn-Bedrock-AgentCore-Runtime-Custom-*"
187
+ headers_input = _prompt_with_default("Enter allowed request headers (comma-separated)", default_headers)
188
+
189
+ if not headers_input:
190
+ _handle_error("At least one request header must be specified for allowlist configuration")
191
+
192
+ # Parse and validate headers
193
+ headers = [header.strip() for header in headers_input.split(",") if header.strip()]
194
+
195
+ if not headers:
196
+ _handle_error("Empty request header allowlist provided")
197
+
198
+ _print_success(f"Request header allowlist configured with {len(headers)} headers")
199
+
200
+ return {"requestHeaderAllowlist": headers}
@@ -176,6 +176,145 @@ class GatewayClient:
176
176
  self.logger.info("\n✅Target is ready")
177
177
  return target
178
178
 
179
+ def fix_iam_permissions(self, gateway: dict) -> None:
180
+ """Fix IAM role trust policy for the gateway.
181
+
182
+ :param gateway: the gateway dict containing roleArn
183
+ """
184
+ # Check for None gateway
185
+ if gateway is None:
186
+ return
187
+
188
+ # Check for missing roleArn
189
+ role_arn = gateway.get("roleArn")
190
+ if not role_arn:
191
+ return
192
+
193
+ sts = boto3.client("sts")
194
+ iam = boto3.client("iam")
195
+
196
+ account_id = sts.get_caller_identity()["Account"]
197
+ role_name = role_arn.split("/")[-1]
198
+
199
+ # Update trust policy
200
+ trust_policy = {
201
+ "Version": "2012-10-17",
202
+ "Statement": [
203
+ {
204
+ "Effect": "Allow",
205
+ "Principal": {"Service": "bedrock-agentcore.amazonaws.com"},
206
+ "Action": "sts:AssumeRole",
207
+ "Condition": {
208
+ "StringEquals": {"aws:SourceAccount": account_id},
209
+ "ArnLike": {"aws:SourceArn": f"arn:aws:bedrock-agentcore:{self.region}:{account_id}:*"},
210
+ },
211
+ }
212
+ ],
213
+ }
214
+
215
+ try:
216
+ iam.update_assume_role_policy(RoleName=role_name, PolicyDocument=json.dumps(trust_policy))
217
+
218
+ # Add Lambda permissions
219
+ iam.put_role_policy(
220
+ RoleName=role_name,
221
+ PolicyName="LambdaInvokePolicy",
222
+ PolicyDocument=json.dumps(
223
+ {
224
+ "Version": "2012-10-17",
225
+ "Statement": [
226
+ {
227
+ "Effect": "Allow",
228
+ "Action": ["lambda:InvokeFunction"],
229
+ "Resource": (
230
+ f"arn:aws:lambda:{self.region}:{account_id}:function:AgentCoreLambdaTestFunction"
231
+ ),
232
+ }
233
+ ],
234
+ }
235
+ ),
236
+ )
237
+ self.logger.info("✓ Fixed IAM permissions for Gateway")
238
+ except Exception as e:
239
+ self.logger.warning("⚠️ IAM role update failed: %s. Continuing with best effort.", str(e))
240
+
241
+ def cleanup_gateway(self, gateway_id: str, client_info: Optional[Dict] = None) -> None:
242
+ """Remove all resources associated with a gateway.
243
+
244
+ :param gateway_id: the ID of the gateway to clean up
245
+ :param client_info: optional Cognito client info for cleanup
246
+ """
247
+ self.logger.info("🧹 Cleaning up Gateway resources...")
248
+
249
+ gateway_client = self.client
250
+
251
+ # Step 1: List and delete all targets
252
+ self.logger.info(" • Finding targets for gateway: %s", gateway_id)
253
+
254
+ try:
255
+ response = gateway_client.list_gateway_targets(gatewayIdentifier=gateway_id)
256
+ # API returns targets in 'items' field
257
+ targets = response.get("items", [])
258
+ self.logger.info(" Found %s targets to delete", len(targets))
259
+
260
+ for target in targets:
261
+ target_id = target["targetId"]
262
+ self.logger.info(" • Deleting target: %s", target_id)
263
+ try:
264
+ gateway_client.delete_gateway_target(gatewayIdentifier=gateway_id, targetId=target_id)
265
+ self.logger.info(" ✓ Target deletion initiated: %s", target_id)
266
+ # Wait for deletion to complete
267
+ time.sleep(5)
268
+ except Exception as e:
269
+ self.logger.warning(" ⚠️ Error deleting target %s: %s", target_id, str(e))
270
+
271
+ # Verify all targets are deleted
272
+ self.logger.info(" • Verifying targets deletion...")
273
+ time.sleep(5) # Additional wait
274
+ verify_response = gateway_client.list_gateway_targets(gatewayIdentifier=gateway_id)
275
+ remaining_targets = verify_response.get("items", [])
276
+ if remaining_targets:
277
+ self.logger.warning(" ⚠️ %s targets still remain", len(remaining_targets))
278
+ else:
279
+ self.logger.info(" ✓ All targets deleted")
280
+
281
+ except Exception as e:
282
+ self.logger.warning(" ⚠️ Error managing targets: %s", str(e))
283
+
284
+ # Step 2: Delete the gateway
285
+ try:
286
+ self.logger.info(" • Deleting gateway: %s", gateway_id)
287
+ gateway_client.delete_gateway(gatewayIdentifier=gateway_id)
288
+ self.logger.info(" ✓ Gateway deleted: %s", gateway_id)
289
+ except Exception as e:
290
+ self.logger.warning(" ⚠️ Error deleting gateway: %s", str(e))
291
+
292
+ # Step 3: Delete Cognito resources if provided
293
+ if client_info and "user_pool_id" in client_info:
294
+ cognito = boto3.client("cognito-idp", region_name=self.region)
295
+ user_pool_id = client_info["user_pool_id"]
296
+
297
+ # Delete domain first
298
+ if "domain_prefix" in client_info:
299
+ domain_prefix = client_info["domain_prefix"]
300
+ self.logger.info(" • Deleting Cognito domain: %s", domain_prefix)
301
+ try:
302
+ cognito.delete_user_pool_domain(UserPoolId=user_pool_id, Domain=domain_prefix)
303
+ self.logger.info(" ✓ Cognito domain deleted")
304
+ time.sleep(5) # Wait for domain deletion
305
+ except Exception as e:
306
+ self.logger.warning(" ⚠️ Error deleting Cognito domain: %s", str(e))
307
+
308
+ # Now delete the user pool
309
+ self.logger.info(" • Deleting Cognito user pool: %s", user_pool_id)
310
+ try:
311
+ cognito.delete_user_pool(UserPoolId=user_pool_id)
312
+ self.logger.info(" ✓ Cognito user pool deleted")
313
+ except Exception as e:
314
+ self.logger.warning(" ⚠️ Error deleting Cognito user pool: %s", str(e))
315
+
316
+ self.logger.info("✅ Cleanup complete")
317
+
179
318
  def __handle_lambda_target_creation(self, role_arn: str) -> Dict[str, Any]:
180
319
  """Create a test lambda.
181
320
 
@@ -0,0 +1 @@
1
+ """BedrockAgentCore Starter Toolkit cli memory package."""
@@ -0,0 +1,98 @@
1
+ """Constants for Bedrock AgentCore Memory SDK."""
2
+
3
+ from enum import Enum
4
+ from typing import Dict, List, Optional
5
+
6
+
7
+ class StrategyType(Enum):
8
+ """Memory strategy types with integrated wrapper key and type methods."""
9
+
10
+ SEMANTIC = "semanticMemoryStrategy"
11
+ SUMMARY = "summaryMemoryStrategy"
12
+ USER_PREFERENCE = "userPreferenceMemoryStrategy"
13
+ CUSTOM = "customMemoryStrategy"
14
+
15
+ def extraction_wrapper_key(self) -> Optional[str]:
16
+ """Get the extraction wrapper key for this strategy type."""
17
+ extraction_keys = {
18
+ StrategyType.SEMANTIC: "semanticExtractionConfiguration",
19
+ StrategyType.USER_PREFERENCE: "userPreferenceExtractionConfiguration",
20
+ }
21
+ return extraction_keys.get(self)
22
+
23
+ def consolidation_wrapper_key(self) -> Optional[str]:
24
+ """Get the consolidation wrapper key for this strategy type."""
25
+ # Only SUMMARY strategy has a consolidation wrapper key
26
+ if self == StrategyType.SUMMARY:
27
+ return "summaryConsolidationConfiguration"
28
+ return None
29
+
30
+ def get_memory_strategy(self) -> str:
31
+ """Get the internal memory strategy type string."""
32
+ strategy_mapping = {
33
+ StrategyType.SEMANTIC: "SEMANTIC",
34
+ StrategyType.SUMMARY: "SUMMARIZATION",
35
+ StrategyType.USER_PREFERENCE: "USER_PREFERENCE",
36
+ StrategyType.CUSTOM: "CUSTOM",
37
+ }
38
+ return strategy_mapping[self]
39
+
40
+ def get_override_type(self) -> Optional[str]:
41
+ """Get the override type for custom strategies."""
42
+ # This method is primarily for CUSTOM strategy type
43
+ # The actual override type would be determined by context
44
+ if self == StrategyType.CUSTOM:
45
+ return "CUSTOM_OVERRIDE" # Base type, specific override determined by usage
46
+ return None
47
+
48
+
49
+ class OverrideType(Enum):
50
+ """Custom strategy override types."""
51
+
52
+ SEMANTIC_OVERRIDE = "SEMANTIC_OVERRIDE"
53
+ SUMMARY_OVERRIDE = "SUMMARY_OVERRIDE"
54
+ USER_PREFERENCE_OVERRIDE = "USER_PREFERENCE_OVERRIDE"
55
+
56
+ def extraction_wrapper_key(self) -> Optional[str]:
57
+ """Get the extraction wrapper key for this override type."""
58
+ extraction_keys = {
59
+ OverrideType.SEMANTIC_OVERRIDE: "semanticExtractionOverride",
60
+ OverrideType.USER_PREFERENCE_OVERRIDE: "userPreferenceExtractionOverride",
61
+ }
62
+ return extraction_keys.get(self)
63
+
64
+ def consolidation_wrapper_key(self) -> Optional[str]:
65
+ """Get the consolidation wrapper key for this override type."""
66
+ consolidation_keys = {
67
+ OverrideType.SEMANTIC_OVERRIDE: "semanticConsolidationOverride",
68
+ OverrideType.SUMMARY_OVERRIDE: "summaryConsolidationOverride",
69
+ OverrideType.USER_PREFERENCE_OVERRIDE: "userPreferenceConsolidationOverride",
70
+ }
71
+ return consolidation_keys.get(self)
72
+
73
+
74
+ class MemoryStatus(Enum):
75
+ """Memory resource statuses."""
76
+
77
+ CREATING = "CREATING"
78
+ ACTIVE = "ACTIVE"
79
+ FAILED = "FAILED"
80
+ UPDATING = "UPDATING"
81
+ DELETING = "DELETING"
82
+
83
+
84
+ class MemoryStrategyStatus(Enum):
85
+ """Memory strategy statuses (new from API update)."""
86
+
87
+ CREATING = "CREATING"
88
+ ACTIVE = "ACTIVE"
89
+ DELETING = "DELETING"
90
+ FAILED = "FAILED"
91
+
92
+
93
+ # Default namespaces for each strategy type
94
+ DEFAULT_NAMESPACES: Dict[StrategyType, List[str]] = {
95
+ StrategyType.SEMANTIC: ["/actor/{actorId}/strategy/{strategyId}/{sessionId}"],
96
+ StrategyType.SUMMARY: ["/actor/{actorId}/strategy/{strategyId}/{sessionId}"],
97
+ StrategyType.USER_PREFERENCE: ["/actor/{actorId}/strategy/{strategyId}"],
98
+ }