bedrock-agentcore-starter-toolkit 0.1.12__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.
- bedrock_agentcore_starter_toolkit/cli/import_agent/agent_info.py +6 -1
- bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +86 -1
- bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py +53 -0
- bedrock_agentcore_starter_toolkit/operations/memory/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/operations/memory/constants.py +98 -0
- bedrock_agentcore_starter_toolkit/operations/memory/manager.py +890 -0
- bedrock_agentcore_starter_toolkit/operations/memory/models/DictWrapper.py +51 -0
- bedrock_agentcore_starter_toolkit/operations/memory/models/Memory.py +17 -0
- bedrock_agentcore_starter_toolkit/operations/memory/models/MemoryStrategy.py +17 -0
- bedrock_agentcore_starter_toolkit/operations/memory/models/MemorySummary.py +17 -0
- bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +4 -0
- bedrock_agentcore_starter_toolkit/operations/runtime/create_role.py +3 -2
- bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py +8 -2
- bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +1 -0
- bedrock_agentcore_starter_toolkit/services/codebuild.py +17 -6
- bedrock_agentcore_starter_toolkit/services/runtime.py +71 -5
- bedrock_agentcore_starter_toolkit/utils/runtime/schema.py +1 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2 +25 -11
- {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/METADATA +3 -4
- {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/RECORD +24 -17
- {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/WHEEL +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/entry_points.txt +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/licenses/LICENSE.txt +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.12.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
|
-
|
|
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)
|
|
@@ -176,6 +176,13 @@ def configure(
|
|
|
176
176
|
authorizer_config: Optional[str] = typer.Option(
|
|
177
177
|
None, "--authorizer-config", "-ac", help="OAuth authorizer configuration as JSON string"
|
|
178
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
|
+
),
|
|
179
186
|
verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output"),
|
|
180
187
|
region: Optional[str] = typer.Option(None, "--region", "-r"),
|
|
181
188
|
protocol: Optional[str] = typer.Option(None, "--protocol", "-p", help="Server protocol (HTTP or MCP)"),
|
|
@@ -243,6 +250,19 @@ def configure(
|
|
|
243
250
|
else:
|
|
244
251
|
oauth_config = config_manager.prompt_oauth_config()
|
|
245
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
|
+
|
|
246
266
|
try:
|
|
247
267
|
result = configure_bedrock_agentcore(
|
|
248
268
|
agent_name=agent_name,
|
|
@@ -254,6 +274,7 @@ def configure(
|
|
|
254
274
|
enable_observability=not disable_otel,
|
|
255
275
|
requirements_file=final_requirements_file,
|
|
256
276
|
authorizer_configuration=oauth_config,
|
|
277
|
+
request_header_configuration=request_header_config,
|
|
257
278
|
verbose=verbose,
|
|
258
279
|
region=region,
|
|
259
280
|
protocol=protocol.upper() if protocol else None,
|
|
@@ -264,6 +285,12 @@ def configure(
|
|
|
264
285
|
if oauth_config:
|
|
265
286
|
auth_info = "OAuth (customJWTAuthorizer)"
|
|
266
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
|
+
|
|
267
294
|
console.print(
|
|
268
295
|
Panel(
|
|
269
296
|
f"[green]Configuration Complete[/green]\n\n"
|
|
@@ -277,7 +304,8 @@ def configure(
|
|
|
277
304
|
f"ECR Repository: [dim]"
|
|
278
305
|
f"{'Auto-create' if result.auto_create_ecr else result.ecr_repository or 'N/A'}"
|
|
279
306
|
f"[/dim]\n"
|
|
280
|
-
f"Authorization: [dim]{auth_info}[/dim]\n
|
|
307
|
+
f"Authorization: [dim]{auth_info}[/dim]\n"
|
|
308
|
+
f"{headers_info}\n"
|
|
281
309
|
f"📄 Config saved to: [dim]{result.config_path}[/dim]\n\n"
|
|
282
310
|
f"[bold]Next Steps:[/bold]\n"
|
|
283
311
|
f" [cyan]agentcore launch[/cyan]",
|
|
@@ -582,6 +610,45 @@ def _show_error_response(error_msg: str):
|
|
|
582
610
|
console.print(f"\n[red]{error_msg}[/red]")
|
|
583
611
|
|
|
584
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
|
+
|
|
585
652
|
def invoke(
|
|
586
653
|
payload: str = typer.Argument(..., help="JSON payload to send"),
|
|
587
654
|
agent: Optional[str] = typer.Option(
|
|
@@ -593,6 +660,12 @@ def invoke(
|
|
|
593
660
|
),
|
|
594
661
|
local_mode: Optional[bool] = typer.Option(False, "--local", "-l", help="Send request to a running local container"),
|
|
595
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
|
+
),
|
|
596
669
|
):
|
|
597
670
|
"""Invoke Bedrock AgentCore endpoint."""
|
|
598
671
|
config_path = Path.cwd() / ".bedrock_agentcore.yaml"
|
|
@@ -625,6 +698,17 @@ def invoke(
|
|
|
625
698
|
"[yellow]Warning: Bearer token provided but OAuth is not configured in .bedrock_agentcore.yaml[/yellow]"
|
|
626
699
|
)
|
|
627
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
|
+
|
|
628
712
|
# Invoke
|
|
629
713
|
result = invoke_bedrock_agentcore(
|
|
630
714
|
config_path=config_path,
|
|
@@ -634,6 +718,7 @@ def invoke(
|
|
|
634
718
|
bearer_token=final_bearer_token,
|
|
635
719
|
user_id=user_id,
|
|
636
720
|
local_mode=local_mode,
|
|
721
|
+
custom_headers=custom_headers,
|
|
637
722
|
)
|
|
638
723
|
agent_display = config.name if config else (agent or "unknown")
|
|
639
724
|
_show_invoke_info_panel(agent_display, result, config)
|
|
@@ -145,3 +145,56 @@ class ConfigurationManager:
|
|
|
145
145
|
|
|
146
146
|
_print_success("OAuth authorizer configuration created")
|
|
147
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}
|
|
@@ -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
|
+
}
|