bedrock-agentcore-starter-toolkit 0.1.26__py3-none-any.whl → 0.1.27__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 (23) hide show
  1. bedrock_agentcore_starter_toolkit/cli/cli.py +9 -1
  2. bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +250 -7
  3. bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py +31 -7
  4. bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py +240 -2
  5. bedrock_agentcore_starter_toolkit/operations/memory/manager.py +20 -33
  6. bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/base.py +2 -0
  7. bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/self_managed.py +107 -0
  8. bedrock_agentcore_starter_toolkit/operations/runtime/__init__.py +4 -0
  9. bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +120 -5
  10. bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py +0 -53
  11. bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +213 -16
  12. bedrock_agentcore_starter_toolkit/operations/runtime/models.py +19 -0
  13. bedrock_agentcore_starter_toolkit/operations/runtime/status.py +30 -0
  14. bedrock_agentcore_starter_toolkit/operations/runtime/stop_session.py +123 -0
  15. bedrock_agentcore_starter_toolkit/operations/runtime/vpc_validation.py +196 -0
  16. bedrock_agentcore_starter_toolkit/services/runtime.py +43 -1
  17. bedrock_agentcore_starter_toolkit/utils/runtime/schema.py +44 -1
  18. {bedrock_agentcore_starter_toolkit-0.1.26.dist-info → bedrock_agentcore_starter_toolkit-0.1.27.dist-info}/METADATA +8 -8
  19. {bedrock_agentcore_starter_toolkit-0.1.26.dist-info → bedrock_agentcore_starter_toolkit-0.1.27.dist-info}/RECORD +23 -20
  20. {bedrock_agentcore_starter_toolkit-0.1.26.dist-info → bedrock_agentcore_starter_toolkit-0.1.27.dist-info}/WHEEL +0 -0
  21. {bedrock_agentcore_starter_toolkit-0.1.26.dist-info → bedrock_agentcore_starter_toolkit-0.1.27.dist-info}/entry_points.txt +0 -0
  22. {bedrock_agentcore_starter_toolkit-0.1.26.dist-info → bedrock_agentcore_starter_toolkit-0.1.27.dist-info}/licenses/LICENSE.txt +0 -0
  23. {bedrock_agentcore_starter_toolkit-0.1.26.dist-info → bedrock_agentcore_starter_toolkit-0.1.27.dist-info}/licenses/NOTICE.txt +0 -0
@@ -6,12 +6,14 @@ from typing import Any, Dict, List, Literal, Optional
6
6
 
7
7
  from ...operations.runtime import (
8
8
  configure_bedrock_agentcore,
9
+ destroy_bedrock_agentcore,
9
10
  get_status,
10
11
  invoke_bedrock_agentcore,
11
12
  launch_bedrock_agentcore,
13
+ stop_runtime_session,
12
14
  validate_agent_name,
13
15
  )
14
- from ...operations.runtime.models import ConfigureResult, LaunchResult, StatusResult
16
+ from ...operations.runtime.models import ConfigureResult, DestroyResult, LaunchResult, StatusResult
15
17
 
16
18
  # Setup centralized logging for SDK usage (notebooks, scripts, imports)
17
19
  from ...utils.logging_config import setup_toolkit_logging
@@ -48,8 +50,13 @@ class Runtime:
48
50
  region: Optional[str] = None,
49
51
  protocol: Optional[Literal["HTTP", "MCP", "A2A"]] = None,
50
52
  disable_otel: bool = False,
51
- memory_mode: Literal["NO_MEMORY", "STM_ONLY", "STM_AND_LTM"] = "STM_ONLY",
53
+ memory_mode: Literal["NO_MEMORY", "STM_ONLY", "STM_AND_LTM"] = "NO_MEMORY",
52
54
  non_interactive: bool = True,
55
+ vpc_enabled: bool = False,
56
+ vpc_subnets: Optional[List[str]] = None,
57
+ vpc_security_groups: Optional[List[str]] = None,
58
+ idle_timeout: Optional[int] = None,
59
+ max_lifetime: Optional[int] = None,
53
60
  ) -> ConfigureResult:
54
61
  """Configure Bedrock AgentCore from notebook using an entrypoint file.
55
62
 
@@ -75,6 +82,11 @@ class Runtime:
75
82
  - "STM_ONLY": Short-term memory only (default)
76
83
  - "STM_AND_LTM": Short-term + long-term memory with strategy extraction
77
84
  non_interactive: Skip interactive prompts and use defaults (default: True)
85
+ vpc_enabled: Enable VPC networking mode (requires vpc_subnets and vpc_security_groups)
86
+ vpc_subnets: List of VPC subnet IDs (required if vpc_enabled=True)
87
+ vpc_security_groups: List of VPC security group IDs (required if vpc_enabled=True)
88
+ idle_timeout: Idle runtime session timeout in seconds (60-28800)
89
+ max_lifetime: Maximum instance lifetime in seconds (60-28800)
78
90
 
79
91
  Returns:
80
92
  ConfigureResult with configuration details
@@ -83,6 +95,14 @@ class Runtime:
83
95
  # Default: STM only (backward compatible)
84
96
  runtime.configure(entrypoint='handler.py')
85
97
 
98
+ # With VPC networking
99
+ runtime.configure(
100
+ entrypoint='handler.py',
101
+ vpc_enabled=True,
102
+ vpc_subnets=['subnet-abc123', 'subnet-def456'],
103
+ vpc_security_groups=['sg-xyz789']
104
+ )
105
+
86
106
  # Explicitly enable LTM
87
107
  runtime.configure(entrypoint='handler.py', memory_mode='STM_AND_LTM')
88
108
 
@@ -91,10 +111,60 @@ class Runtime:
91
111
 
92
112
  # Invalid - raises error
93
113
  runtime.configure(entrypoint='handler.py', disable_memory=True, memory_mode='STM_AND_LTM')
114
+
115
+ # With lifecycle settings
116
+ runtime.configure(
117
+ entrypoint='handler.py',
118
+ idle_timeout=1800, # 30 minutes
119
+ max_lifetime=7200 # 2 hours
120
+ )
94
121
  """
95
122
  if protocol and protocol.upper() not in ["HTTP", "MCP", "A2A"]:
96
123
  raise ValueError("protocol must be either HTTP or MCP or A2A")
97
124
 
125
+ # Validate VPC configuration
126
+ if vpc_enabled:
127
+ if not vpc_subnets or not vpc_security_groups:
128
+ raise ValueError(
129
+ "VPC mode requires both vpc_subnets and vpc_security_groups.\n"
130
+ "Example: runtime.configure(entrypoint='handler.py', vpc_enabled=True, "
131
+ "vpc_subnets=['subnet-abc123', 'subnet-def456'], "
132
+ "vpc_security_groups=['sg-xyz789'])"
133
+ )
134
+
135
+ # Validate subnet ID format - UPDATED
136
+ for subnet_id in vpc_subnets:
137
+ if not subnet_id.startswith("subnet-"):
138
+ raise ValueError(f"Invalid subnet ID format: {subnet_id}\nSubnet IDs must start with 'subnet-'")
139
+ if len(subnet_id) < 15: # "subnet-" + 8 chars minimum
140
+ raise ValueError(
141
+ f"Invalid subnet ID format: {subnet_id}\n"
142
+ f"Subnet ID is too short. Expected: subnet-xxxxxxxx (at least 8 hex chars)"
143
+ )
144
+
145
+ # Validate security group ID format - UPDATED
146
+ for sg_id in vpc_security_groups:
147
+ if not sg_id.startswith("sg-"):
148
+ raise ValueError(
149
+ f"Invalid security group ID format: {sg_id}\nSecurity group IDs must start with 'sg-'"
150
+ )
151
+ if len(sg_id) < 11: # "sg-" + 8 chars minimum
152
+ raise ValueError(
153
+ f"Invalid security group ID format: {sg_id}\n"
154
+ f"Security group ID is too short. Expected: sg-xxxxxxxx (at least 8 hex chars)"
155
+ )
156
+
157
+ log.info(
158
+ "VPC mode enabled with %d subnets and %d security groups", len(vpc_subnets), len(vpc_security_groups)
159
+ )
160
+
161
+ elif vpc_subnets or vpc_security_groups:
162
+ raise ValueError(
163
+ "vpc_subnets and vpc_security_groups require vpc_enabled=True.\n"
164
+ "Use: runtime.configure(entrypoint='handler.py', vpc_enabled=True, "
165
+ "vpc_subnets=[...], vpc_security_groups=[...])"
166
+ )
167
+
98
168
  # Parse entrypoint to get agent name
99
169
  file_path, file_name = parse_entrypoint(entrypoint)
100
170
  agent_name = agent_name or file_name
@@ -152,6 +222,11 @@ class Runtime:
152
222
  region=region,
153
223
  protocol=protocol.upper() if protocol else None,
154
224
  non_interactive=non_interactive,
225
+ vpc_enabled=vpc_enabled,
226
+ vpc_subnets=vpc_subnets,
227
+ vpc_security_groups=vpc_security_groups,
228
+ idle_timeout=idle_timeout,
229
+ max_lifetime=max_lifetime,
155
230
  )
156
231
 
157
232
  self._config_path = result.config_path
@@ -319,6 +394,36 @@ class Runtime:
319
394
  )
320
395
  return result.response
321
396
 
397
+ def stop_session(self, session_id: Optional[str] = None) -> Dict[str, Any]:
398
+ """Stop an active runtime session.
399
+
400
+ Args:
401
+ session_id: Optional session ID to stop. If not provided, uses tracked session.
402
+
403
+ Returns:
404
+ Dictionary with stop session result details
405
+
406
+ Raises:
407
+ ValueError: If no session ID provided or found, or agent not configured
408
+ """
409
+ if not self._config_path:
410
+ log.warning("Agent not configured")
411
+ log.info("Call .configure() first to set up your agent")
412
+ raise ValueError("Must configure first. Call .configure() first.")
413
+
414
+ result = stop_runtime_session(
415
+ config_path=self._config_path,
416
+ session_id=session_id,
417
+ )
418
+
419
+ log.info("Session stopped: %s", result.session_id)
420
+ return {
421
+ "session_id": result.session_id,
422
+ "agent_name": result.agent_name,
423
+ "status_code": result.status_code,
424
+ "message": result.message,
425
+ }
426
+
322
427
  def status(self) -> StatusResult:
323
428
  """Get Bedrock AgentCore status including config and runtime details.
324
429
 
@@ -335,6 +440,76 @@ class Runtime:
335
440
  log.info("Retrieved Bedrock AgentCore status for: %s", self.name or "Bedrock AgentCore")
336
441
  return result
337
442
 
443
+ def destroy(
444
+ self,
445
+ dry_run: bool = False,
446
+ delete_ecr_repo: bool = False,
447
+ ) -> DestroyResult:
448
+ """Destroy Bedrock AgentCore resources from notebook.
449
+
450
+ Args:
451
+ dry_run: If True, only show what would be destroyed without actually doing it
452
+ delete_ecr_repo: If True, also delete the ECR repository after removing images
453
+
454
+ Returns:
455
+ DestroyResult with details of what was destroyed or would be destroyed
456
+
457
+ Example:
458
+ # Preview what would be destroyed
459
+ result = runtime.destroy(dry_run=True)
460
+
461
+ # Destroy resources (keeping ECR repository)
462
+ result = runtime.destroy()
463
+
464
+ # Destroy resources including ECR repository
465
+ result = runtime.destroy(delete_ecr_repo=True)
466
+ """
467
+ if not self._config_path:
468
+ log.warning("Configuration not found")
469
+ log.info("Call .configure() first to set up your agent")
470
+ log.info("Example: runtime.configure(entrypoint='my_agent.py')")
471
+ raise ValueError("Must configure first. Call .configure() first.")
472
+
473
+ if dry_run:
474
+ log.info("🔍 Dry run mode: showing what would be destroyed")
475
+ else:
476
+ log.info("🗑️ Destroying Bedrock AgentCore resources")
477
+ if delete_ecr_repo:
478
+ log.info(" • Including ECR repository deletion")
479
+
480
+ try:
481
+ result = destroy_bedrock_agentcore(
482
+ config_path=self._config_path,
483
+ agent_name=self.name,
484
+ dry_run=dry_run,
485
+ force=True, # Always force in notebook interface to avoid interactive prompts
486
+ delete_ecr_repo=delete_ecr_repo,
487
+ )
488
+
489
+ # Log summary
490
+ if dry_run:
491
+ log.info("Dry run completed. Would destroy %d resources", len(result.resources_removed))
492
+ else:
493
+ log.info("Destroy completed. Removed %d resources", len(result.resources_removed))
494
+
495
+ # Clear our internal state if destruction was successful and not a dry run
496
+ if not result.errors:
497
+ self._config_path = None
498
+ self.name = None
499
+
500
+ # Log warnings and errors
501
+ for warning in result.warnings:
502
+ log.warning("⚠️ %s", warning)
503
+
504
+ for error in result.errors:
505
+ log.error("❌ %s", error)
506
+
507
+ return result
508
+
509
+ except Exception as e:
510
+ log.error("Destroy operation failed: %s", str(e))
511
+ raise
512
+
338
513
  def help_deployment_modes(self):
339
514
  """Display information about available deployment modes and migration guidance."""
340
515
  print("\n🚀 Bedrock AgentCore Deployment Modes:")
@@ -370,3 +545,66 @@ class Runtime:
370
545
  print(" runtime.launch() # Uses CodeBuild by default")
371
546
  print(' runtime.invoke({"prompt": "Hello"})')
372
547
  print()
548
+
549
+ def help_vpc_networking(self):
550
+ """Display information about VPC networking configuration."""
551
+ print("\n🔒 VPC Networking for Bedrock AgentCore")
552
+ print("=" * 50)
553
+
554
+ print("\n📋 What is VPC Networking?")
555
+ print(" VPC (Virtual Private Cloud) mode allows your agent to:")
556
+ print(" • Access private resources (databases, internal APIs)")
557
+ print(" • Run in isolated network environments")
558
+ print(" • Comply with enterprise security requirements")
559
+
560
+ print("\n⚙️ Prerequisites:")
561
+ print(" You must have existing AWS resources:")
562
+ print(" • VPC with private subnets")
563
+ print(" • Security groups with appropriate rules")
564
+ print(" • (Optional) NAT Gateway for internet access")
565
+ print(" • (Optional) VPC endpoints for AWS services")
566
+
567
+ print("\n🚀 Basic Usage:")
568
+ print(" runtime.configure(")
569
+ print(" entrypoint='my_agent.py',")
570
+ print(" vpc_enabled=True,")
571
+ print(" vpc_subnets=['subnet-abc123', 'subnet-def456'],")
572
+ print(" vpc_security_groups=['sg-xyz789']")
573
+ print(" )")
574
+ print(" runtime.launch()")
575
+
576
+ print("\n📝 Requirements:")
577
+ print(" • All subnets must be in the same VPC")
578
+ print(" • Security groups must be in the same VPC as subnets")
579
+ print(" • Use subnets from multiple AZs for high availability")
580
+ print(" • Security groups must allow outbound HTTPS (443) traffic")
581
+
582
+ print("\n⚠️ Important Notes:")
583
+ print(" • Network configuration is IMMUTABLE after agent creation")
584
+ print(" • Cannot migrate existing PUBLIC agents to VPC mode")
585
+ print(" • Create a new agent if you need to change network settings")
586
+ print(" • Without NAT gateway, agent cannot pull container images")
587
+
588
+ print("\n🔍 Security Group Requirements:")
589
+ print(" Your security groups must allow:")
590
+ print(" • Outbound HTTPS (443) - for AWS API calls")
591
+ print(" • Outbound to your private resources (as needed)")
592
+ print(" • Inbound rules are typically not required")
593
+
594
+ print("\n💡 Example with All Features:")
595
+ print(" runtime.configure(")
596
+ print(" entrypoint='my_agent.py',")
597
+ print(" execution_role='arn:aws:iam::123456789012:role/MyRole',")
598
+ print(" vpc_enabled=True,")
599
+ print(" vpc_subnets=['subnet-abc123', 'subnet-def456'],")
600
+ print(" vpc_security_groups=['sg-xyz789'],")
601
+ print(" memory_mode='STM_AND_LTM'")
602
+ print(" )")
603
+
604
+ print("\n📚 Related Commands:")
605
+ print(" runtime.status() # View network configuration")
606
+ print(" runtime.help_deployment_modes() # Deployment options")
607
+
608
+ print("\n🔗 More Information:")
609
+ print(" See AWS VPC documentation for networking setup")
610
+ print()
@@ -9,6 +9,7 @@ from typing import Any, Dict, List, Optional, Union
9
9
  import boto3
10
10
  from botocore.config import Config as BotocoreConfig
11
11
  from botocore.exceptions import ClientError
12
+ from rich.console import Console
12
13
 
13
14
  from .constants import MemoryStatus, MemoryStrategyStatus, OverrideType, StrategyType
14
15
  from .models import convert_strategies_to_dicts
@@ -32,6 +33,7 @@ class MemoryManager:
32
33
  region_name: Optional[str] = None,
33
34
  boto3_session: Optional[boto3.Session] = None,
34
35
  boto_client_config: Optional[BotocoreConfig] = None,
36
+ console: Optional[Console] = None,
35
37
  ):
36
38
  """Initialize MemoryManager with AWS region.
37
39
 
@@ -42,12 +44,14 @@ class MemoryManager:
42
44
  parameter is also specified, validation will ensure they match.
43
45
  boto_client_config: Optional boto3 client configuration. If provided, will be
44
46
  merged with default configuration including user agent.
47
+ console: Optional Rich console instance for output (creates new if not provided)
45
48
 
46
49
  Raises:
47
50
  ValueError: If region_name parameter conflicts with boto3_session region.
48
51
  """
49
52
  session = boto3_session or boto3.Session()
50
53
  session_region = session.region_name
54
+ self.console = console or Console()
51
55
 
52
56
  # Validate region consistency if both are provided
53
57
  if region_name and boto3_session and session_region and region_name != session_region:
@@ -280,32 +284,7 @@ class MemoryManager:
280
284
  if memory_id is None:
281
285
  memory_id = ""
282
286
  logger.info("Created memory %s, waiting for ACTIVE status...", memory_id)
283
-
284
- start_time = time.time()
285
- while time.time() - start_time < max_wait:
286
- elapsed = int(time.time() - start_time)
287
-
288
- try:
289
- status = self.get_memory_status(memory_id)
290
-
291
- if status == MemoryStatus.ACTIVE.value:
292
- logger.info("Memory %s is now ACTIVE (took %d seconds)", memory_id, elapsed)
293
- return memory
294
- elif status == MemoryStatus.FAILED.value:
295
- # Get failure reason if available
296
- response = self._control_plane_client.get_memory(memoryId=memory_id)
297
- failure_reason = response["memory"].get("failureReason", "Unknown")
298
- raise RuntimeError("Memory creation failed: %s" % failure_reason)
299
- else:
300
- logger.debug("Memory status: %s (%d seconds elapsed)", status, elapsed)
301
-
302
- except ClientError as e:
303
- logger.error("Error checking memory status: %s", e)
304
- raise
305
-
306
- time.sleep(poll_interval)
307
-
308
- raise TimeoutError(f"Memory {memory_id} did not become ACTIVE within {max_wait} seconds")
287
+ return self._wait_for_memory_active(memory_id, max_wait, poll_interval)
309
288
 
310
289
  def create_memory_and_wait(
311
290
  self,
@@ -1008,6 +987,8 @@ class MemoryManager:
1008
987
  )
1009
988
 
1010
989
  start_time = time.time()
990
+ last_status_print = 0
991
+ status_print_interval = 10 # Print status every 10 seconds
1011
992
 
1012
993
  while time.time() - start_time < max_wait:
1013
994
  elapsed = int(time.time() - start_time)
@@ -1029,13 +1010,18 @@ class MemoryManager:
1029
1010
  self._check_strategies_terminal_state(strategies)
1030
1011
  )
1031
1012
 
1032
- # Log current status
1033
- logger.debug(
1034
- "Memory status: %s, Strategy statuses: %s (%d seconds elapsed)",
1035
- memory_status,
1036
- strategy_statuses,
1037
- elapsed,
1038
- )
1013
+ # Print status update every 10 seconds
1014
+ if elapsed - last_status_print >= status_print_interval:
1015
+ if strategies:
1016
+ active_count = len([s for s in strategy_statuses if s == "ACTIVE"])
1017
+ self.console.log(
1018
+ f" ⏳ Memory: {memory_status}, "
1019
+ f"Strategies: {active_count}/{len(strategies)} active "
1020
+ f"({elapsed}s elapsed)"
1021
+ )
1022
+ else:
1023
+ self.console.log(f" ⏳ Memory: {memory_status} ({elapsed}s elapsed)")
1024
+ last_status_print = elapsed
1039
1025
 
1040
1026
  # Check if memory is ACTIVE and all strategies are in terminal states
1041
1027
  if memory_status == MemoryStatus.ACTIVE.value and all_strategies_terminal:
@@ -1048,6 +1034,7 @@ class MemoryManager:
1048
1034
  memory_id,
1049
1035
  elapsed,
1050
1036
  )
1037
+ self.console.log(f" ✅ Memory is ACTIVE (took {elapsed}s)")
1051
1038
  return Memory(memory)
1052
1039
 
1053
1040
  # Wait before next check
@@ -7,6 +7,7 @@ from pydantic import BaseModel, ConfigDict, Field
7
7
 
8
8
  if TYPE_CHECKING:
9
9
  from .custom import CustomSemanticStrategy, CustomSummaryStrategy, CustomUserPreferenceStrategy
10
+ from .self_managed import SelfManagedStrategy
10
11
  from .semantic import SemanticStrategy
11
12
  from .summary import SummaryStrategy
12
13
  from .user_preference import UserPreferenceStrategy
@@ -73,5 +74,6 @@ StrategyType = Union[
73
74
  "CustomSummaryStrategy",
74
75
  "CustomUserPreferenceStrategy",
75
76
  "UserPreferenceStrategy",
77
+ "SelfManagedStrategy",
76
78
  Dict[str, Any], # Backward compatibility with dict-based strategies
77
79
  ]
@@ -0,0 +1,107 @@
1
+ """Self managed memory strategy implementation."""
2
+
3
+ from typing import Any, Dict, List, Union
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+ from .base import BaseStrategy
8
+
9
+
10
+ class MessageBasedTrigger(BaseModel):
11
+ """Trigger configuration based on message."""
12
+
13
+ message_count: int = Field(default=6, description="Number of messages that trigger memory processing.")
14
+
15
+
16
+ class TokenBasedTrigger(BaseModel):
17
+ """Trigger configuration based on tokens."""
18
+
19
+ token_count: int = Field(default=5000, description="Number of tokens that trigger memory processing.")
20
+
21
+
22
+ class TimeBasedTrigger(BaseModel):
23
+ """Trigger configuration based on time."""
24
+
25
+ idle_session_timeout: int = Field(
26
+ default=20, description="Idle session timeout (seconds) that triggers memory processing."
27
+ )
28
+
29
+
30
+ class InvocationConfig(BaseModel):
31
+ """Configuration to invoke customer-owned memory processing pipeline."""
32
+
33
+ topic_arn: str = Field(..., description="The ARN of the SNS topic for job notifications.")
34
+ payload_delivery_bucket_name: str = Field(..., description="S3 bucket name for event payload delivery.")
35
+
36
+
37
+ class SelfManagedStrategy(BaseStrategy):
38
+ """Self-managed memory strategy with custom processing pipeline.
39
+
40
+ This strategy allows complete control over memory processing through
41
+ customer-owned pipelines triggered by configurable conditions.
42
+
43
+ Attributes:
44
+ trigger_conditions: List of conditions that trigger memory processing
45
+ invocation_config: Configuration for invoking memory processing pipeline
46
+ historical_context_window_size: Number of historical messages to include
47
+
48
+ Example:
49
+ strategy = SelfManagedStrategy(
50
+ name="SelfManagedStrategy",
51
+ description="Self-managed processing with SNS notifications",
52
+ trigger_conditions=[
53
+ MessageBasedTrigger(message_count=10),
54
+ TokenBasedTrigger(token_count=8000)
55
+ ],
56
+ invocation_config=InvocationConfig(
57
+ topic_arn="arn:aws:sns:us-east-1:123456789012:memory-processing",
58
+ payload_delivery_bucket_name="my-memory-bucket"
59
+ ),
60
+ historical_context_window_size=6
61
+ )
62
+ """
63
+
64
+ trigger_conditions: List[Union[MessageBasedTrigger, TokenBasedTrigger, TimeBasedTrigger]] = Field(
65
+ default_factory=list
66
+ )
67
+ invocation_config: InvocationConfig
68
+ historical_context_window_size: int = Field(
69
+ default=4, description="Number of historical messages to include in processing context."
70
+ )
71
+
72
+ def to_dict(self) -> Dict[str, Any]:
73
+ """Convert to dictionary format for API calls."""
74
+ config = {
75
+ "name": self.name,
76
+ "configuration": {
77
+ "selfManagedConfiguration": {
78
+ "triggerConditions": self._convert_trigger_conditions(),
79
+ "invocationConfiguration": self._convert_invocation_config(),
80
+ "historicalContextWindowSize": self.historical_context_window_size,
81
+ }
82
+ },
83
+ }
84
+
85
+ if self.description is not None:
86
+ config["description"] = self.description
87
+
88
+ return {"customMemoryStrategy": config}
89
+
90
+ def _convert_trigger_conditions(self) -> List[Dict[str, Any]]:
91
+ """Convert trigger conditions to API format."""
92
+ conditions = []
93
+ for condition in self.trigger_conditions:
94
+ if isinstance(condition, MessageBasedTrigger):
95
+ conditions.append({"messageBasedTrigger": {"messageCount": condition.message_count}})
96
+ elif isinstance(condition, TokenBasedTrigger):
97
+ conditions.append({"tokenBasedTrigger": {"tokenCount": condition.token_count}})
98
+ elif isinstance(condition, TimeBasedTrigger):
99
+ conditions.append({"timeBasedTrigger": {"idleSessionTimeout": condition.idle_session_timeout}})
100
+ return conditions
101
+
102
+ def _convert_invocation_config(self) -> Dict[str, Any]:
103
+ """Convert invocation config to API format."""
104
+ return {
105
+ "topicArn": self.invocation_config.topic_arn,
106
+ "payloadDeliveryBucketName": self.invocation_config.payload_delivery_bucket_name,
107
+ }
@@ -18,8 +18,10 @@ from .models import (
18
18
  LaunchResult,
19
19
  StatusConfigInfo,
20
20
  StatusResult,
21
+ StopSessionResult,
21
22
  )
22
23
  from .status import get_status
24
+ from .stop_session import stop_runtime_session
23
25
 
24
26
  __all__ = [
25
27
  "configure_bedrock_agentcore",
@@ -31,6 +33,7 @@ __all__ = [
31
33
  "infer_agent_name",
32
34
  "launch_bedrock_agentcore",
33
35
  "invoke_bedrock_agentcore",
36
+ "stop_runtime_session",
34
37
  "get_status",
35
38
  "ConfigureResult",
36
39
  "DestroyResult",
@@ -38,4 +41,5 @@ __all__ = [
38
41
  "LaunchResult",
39
42
  "StatusResult",
40
43
  "StatusConfigInfo",
44
+ "StopSessionResult",
41
45
  ]