bedrock-agentcore-starter-toolkit 0.1.10__py3-none-any.whl → 0.1.11__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/cli.py +1 -1
- bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +50 -43
- bedrock_agentcore_starter_toolkit/operations/runtime/destroy.py +61 -56
- bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py +1 -1
- bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +15 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/scripts/base_bedrock_translate.py +4 -3
- bedrock_agentcore_starter_toolkit/services/import_agent/utils.py +14 -0
- bedrock_agentcore_starter_toolkit/services/runtime.py +5 -1
- bedrock_agentcore_starter_toolkit/services/xray.py +161 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/logs.py +12 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2 +11 -25
- {bedrock_agentcore_starter_toolkit-0.1.10.dist-info → bedrock_agentcore_starter_toolkit-0.1.11.dist-info}/METADATA +2 -2
- {bedrock_agentcore_starter_toolkit-0.1.10.dist-info → bedrock_agentcore_starter_toolkit-0.1.11.dist-info}/RECORD +17 -16
- {bedrock_agentcore_starter_toolkit-0.1.10.dist-info → bedrock_agentcore_starter_toolkit-0.1.11.dist-info}/WHEEL +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.10.dist-info → bedrock_agentcore_starter_toolkit-0.1.11.dist-info}/entry_points.txt +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.10.dist-info → bedrock_agentcore_starter_toolkit-0.1.11.dist-info}/licenses/LICENSE.txt +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.10.dist-info → bedrock_agentcore_starter_toolkit-0.1.11.dist-info}/licenses/NOTICE.txt +0 -0
|
@@ -5,7 +5,7 @@ import typer
|
|
|
5
5
|
from ..cli.gateway.commands import create_mcp_gateway, create_mcp_gateway_target, gateway_app
|
|
6
6
|
from ..utils.logging_config import setup_toolkit_logging
|
|
7
7
|
from .import_agent.commands import import_agent
|
|
8
|
-
from .runtime.commands import configure_app, invoke, launch, status
|
|
8
|
+
from .runtime.commands import configure_app, destroy, invoke, launch, status
|
|
9
9
|
|
|
10
10
|
app = typer.Typer(name="agentcore", help="BedrockAgentCore CLI", add_completion=False, rich_markup_mode="rich")
|
|
11
11
|
|
|
@@ -20,7 +20,9 @@ from ...operations.runtime import (
|
|
|
20
20
|
launch_bedrock_agentcore,
|
|
21
21
|
validate_agent_name,
|
|
22
22
|
)
|
|
23
|
+
from ...utils.runtime.config import load_config
|
|
23
24
|
from ...utils.runtime.entrypoint import parse_entrypoint
|
|
25
|
+
from ...utils.runtime.logs import get_agent_log_paths, get_aws_tail_commands, get_genai_observability_url
|
|
24
26
|
from ..common import _handle_error, _print_success, console
|
|
25
27
|
from .configuration_manager import ConfigurationManager
|
|
26
28
|
|
|
@@ -112,8 +114,6 @@ def list_agents():
|
|
|
112
114
|
"""List configured agents."""
|
|
113
115
|
config_path = Path.cwd() / ".bedrock_agentcore.yaml"
|
|
114
116
|
try:
|
|
115
|
-
from ...utils.runtime.config import load_config
|
|
116
|
-
|
|
117
117
|
project_config = load_config(config_path)
|
|
118
118
|
if not project_config.agents:
|
|
119
119
|
console.print("[yellow]No agents configured.[/yellow]")
|
|
@@ -404,6 +404,8 @@ def launch(
|
|
|
404
404
|
auto_update_on_conflict=auto_update_on_conflict,
|
|
405
405
|
)
|
|
406
406
|
|
|
407
|
+
project_config = load_config(config_path)
|
|
408
|
+
agent_config = project_config.get_agent_config(agent)
|
|
407
409
|
# Handle result based on mode
|
|
408
410
|
if result.mode == "local":
|
|
409
411
|
_print_success(f"Docker image built: {result.tag}")
|
|
@@ -422,6 +424,10 @@ def launch(
|
|
|
422
424
|
elif result.mode == "codebuild":
|
|
423
425
|
# Show deployment success panel
|
|
424
426
|
agent_name = result.tag.split(":")[0].replace("bedrock_agentcore-", "")
|
|
427
|
+
|
|
428
|
+
# Get region from configuration
|
|
429
|
+
region = agent_config.aws.region if agent_config else "us-east-1"
|
|
430
|
+
|
|
425
431
|
deploy_panel = (
|
|
426
432
|
f"✅ [green]CodeBuild Deployment Successful![/green]\n\n"
|
|
427
433
|
f"[bold]Agent Details:[/bold]\n"
|
|
@@ -437,18 +443,18 @@ def launch(
|
|
|
437
443
|
|
|
438
444
|
# Add log information if we have agent_id
|
|
439
445
|
if result.agent_id:
|
|
440
|
-
from ...utils.runtime.logs import get_agent_log_paths, get_aws_tail_commands
|
|
441
|
-
|
|
442
446
|
runtime_logs, otel_logs = get_agent_log_paths(result.agent_id)
|
|
443
447
|
follow_cmd, since_cmd = get_aws_tail_commands(runtime_logs)
|
|
444
|
-
deploy_panel +=
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
448
|
+
deploy_panel += f"\n\n📋 [cyan]CloudWatch Logs:[/cyan]\n {runtime_logs}\n {otel_logs}\n\n"
|
|
449
|
+
# Only show GenAI Observability Dashboard if OTEL is enabled
|
|
450
|
+
if agent_config and agent_config.aws.observability.enabled:
|
|
451
|
+
deploy_panel += (
|
|
452
|
+
f"🔍 [cyan]GenAI Observability Dashboard:[/cyan]\n"
|
|
453
|
+
f" {get_genai_observability_url(region)}\n\n"
|
|
454
|
+
f"⏱️ [dim]Note: Observability data may take up to 10 minutes to appear "
|
|
455
|
+
f"after first launch[/dim]\n\n"
|
|
456
|
+
)
|
|
457
|
+
deploy_panel += f"💡 [dim]Tail logs with:[/dim]\n {follow_cmd}\n {since_cmd}"
|
|
452
458
|
|
|
453
459
|
console.print(
|
|
454
460
|
Panel(
|
|
@@ -483,8 +489,6 @@ def launch(
|
|
|
483
489
|
)
|
|
484
490
|
|
|
485
491
|
if result.agent_id:
|
|
486
|
-
from ...utils.runtime.logs import get_agent_log_paths, get_aws_tail_commands
|
|
487
|
-
|
|
488
492
|
runtime_logs, otel_logs = get_agent_log_paths(result.agent_id)
|
|
489
493
|
follow_cmd, since_cmd = get_aws_tail_commands(runtime_logs)
|
|
490
494
|
deploy_panel += (
|
|
@@ -530,15 +534,17 @@ def _show_invoke_info_panel(agent_name: str, invoke_result=None, config=None):
|
|
|
530
534
|
# Agent ARN
|
|
531
535
|
if invoke_result and invoke_result.agent_arn:
|
|
532
536
|
info_lines.append(f"ARN: [cyan]{invoke_result.agent_arn}[/cyan]")
|
|
533
|
-
# CloudWatch logs (if we have config with agent_id)
|
|
537
|
+
# CloudWatch logs and GenAI Observability Dashboard (if we have config with agent_id)
|
|
534
538
|
if config and hasattr(config, "bedrock_agentcore") and config.bedrock_agentcore.agent_id:
|
|
535
539
|
try:
|
|
536
|
-
from ...utils.runtime.logs import get_agent_log_paths, get_aws_tail_commands
|
|
537
|
-
|
|
538
540
|
runtime_logs, _ = get_agent_log_paths(config.bedrock_agentcore.agent_id)
|
|
539
541
|
follow_cmd, since_cmd = get_aws_tail_commands(runtime_logs)
|
|
540
542
|
info_lines.append(f"Logs: {follow_cmd}")
|
|
541
543
|
info_lines.append(f" {since_cmd}")
|
|
544
|
+
|
|
545
|
+
# Only show GenAI Observability Dashboard if OTEL is enabled
|
|
546
|
+
if config.aws.observability.enabled:
|
|
547
|
+
info_lines.append(f"GenAI Dashboard: {get_genai_observability_url(config.aws.region)}")
|
|
542
548
|
except Exception:
|
|
543
549
|
pass # nosec B110
|
|
544
550
|
panel_content = "\n".join(info_lines) if info_lines else "Invoke information unavailable"
|
|
@@ -580,8 +586,6 @@ def invoke(
|
|
|
580
586
|
config_path = Path.cwd() / ".bedrock_agentcore.yaml"
|
|
581
587
|
|
|
582
588
|
try:
|
|
583
|
-
from ...utils.runtime.config import load_config
|
|
584
|
-
|
|
585
589
|
# Load project configuration to check if auth is configured
|
|
586
590
|
project_config = load_config(config_path)
|
|
587
591
|
config = project_config.get_agent_config(agent)
|
|
@@ -671,8 +675,6 @@ def invoke(
|
|
|
671
675
|
agent_name = config.name if config else (agent or "unknown")
|
|
672
676
|
except (NameError, AttributeError):
|
|
673
677
|
try:
|
|
674
|
-
from ...utils.runtime.config import load_config
|
|
675
|
-
|
|
676
678
|
fallback_project_config = load_config(config_path)
|
|
677
679
|
agent_config = fallback_project_config.get_agent_config(agent)
|
|
678
680
|
agent_name = agent_config.name if agent_config else (agent or "unknown")
|
|
@@ -777,20 +779,24 @@ def status(
|
|
|
777
779
|
agent_id = status_json.get("config", {}).get("agent_id")
|
|
778
780
|
if agent_id:
|
|
779
781
|
try:
|
|
780
|
-
from ...utils.runtime.logs import get_agent_log_paths, get_aws_tail_commands
|
|
781
|
-
|
|
782
782
|
endpoint_name = endpoint_data.get("name")
|
|
783
783
|
runtime_logs, otel_logs = get_agent_log_paths(agent_id, endpoint_name)
|
|
784
784
|
follow_cmd, since_cmd = get_aws_tail_commands(runtime_logs)
|
|
785
785
|
|
|
786
|
-
panel_content +=
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
786
|
+
panel_content += f"📋 [cyan]CloudWatch Logs:[/cyan]\n {runtime_logs}\n {otel_logs}\n\n"
|
|
787
|
+
|
|
788
|
+
# Only show GenAI Observability Dashboard if OTEL is enabled
|
|
789
|
+
project_config = load_config(config_path)
|
|
790
|
+
agent_config = project_config.get_agent_config(agent)
|
|
791
|
+
if agent_config and agent_config.aws.observability.enabled:
|
|
792
|
+
panel_content += (
|
|
793
|
+
f"🔍 [cyan]GenAI Observability Dashboard:[/cyan]\n"
|
|
794
|
+
f" {get_genai_observability_url(status_json['config']['region'])}\n\n"
|
|
795
|
+
f"⏱️ [dim]Note: Observability data may take up to 10 minutes to appear "
|
|
796
|
+
f"after first launch[/dim]\n\n"
|
|
797
|
+
)
|
|
798
|
+
|
|
799
|
+
panel_content += f"💡 [dim]Tail logs with:[/dim]\n {follow_cmd}\n {since_cmd}\n\n"
|
|
794
800
|
except Exception: # nosec B110
|
|
795
801
|
# If log retrieval fails, continue without logs section
|
|
796
802
|
pass
|
|
@@ -870,15 +876,13 @@ def destroy(
|
|
|
870
876
|
dry_run: bool = typer.Option(
|
|
871
877
|
False, "--dry-run", help="Show what would be destroyed without actually destroying anything"
|
|
872
878
|
),
|
|
873
|
-
force: bool = typer.Option(
|
|
874
|
-
False, "--force", help="Skip confirmation prompts and destroy immediately"
|
|
875
|
-
),
|
|
879
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation prompts and destroy immediately"),
|
|
876
880
|
delete_ecr_repo: bool = typer.Option(
|
|
877
881
|
False, "--delete-ecr-repo", help="Also delete the ECR repository after removing images"
|
|
878
882
|
),
|
|
879
883
|
) -> None:
|
|
880
884
|
"""Destroy Bedrock AgentCore resources.
|
|
881
|
-
|
|
885
|
+
|
|
882
886
|
This command removes the following AWS resources for the specified agent:
|
|
883
887
|
- Bedrock AgentCore endpoint (if exists)
|
|
884
888
|
- Bedrock AgentCore agent runtime
|
|
@@ -887,26 +891,27 @@ def destroy(
|
|
|
887
891
|
- IAM execution role (only if not used by other agents)
|
|
888
892
|
- Agent deployment configuration
|
|
889
893
|
- ECR repository (only if --delete-ecr-repo is specified)
|
|
890
|
-
|
|
894
|
+
|
|
891
895
|
CAUTION: This action cannot be undone. Use --dry-run to preview changes first.
|
|
892
896
|
"""
|
|
893
897
|
config_path = Path.cwd() / ".bedrock_agentcore.yaml"
|
|
894
898
|
|
|
895
899
|
try:
|
|
896
|
-
from ...utils.runtime.config import load_config
|
|
897
|
-
|
|
898
900
|
# Load project configuration to get agent details
|
|
899
901
|
project_config = load_config(config_path)
|
|
900
902
|
agent_config = project_config.get_agent_config(agent)
|
|
901
|
-
|
|
903
|
+
|
|
902
904
|
if not agent_config:
|
|
903
905
|
_handle_error(f"Agent '{agent or 'default'}' not found in configuration")
|
|
904
|
-
|
|
906
|
+
|
|
905
907
|
actual_agent_name = agent_config.name
|
|
906
908
|
|
|
907
909
|
# Show what will be destroyed
|
|
908
910
|
if dry_run:
|
|
909
|
-
console.print(
|
|
911
|
+
console.print(
|
|
912
|
+
f"[cyan]🔍 Dry run: Preview of resources that would be destroyed for agent "
|
|
913
|
+
f"'{actual_agent_name}'[/cyan]\n"
|
|
914
|
+
)
|
|
910
915
|
else:
|
|
911
916
|
console.print(f"[yellow]⚠️ About to destroy resources for agent '{actual_agent_name}'[/yellow]\n")
|
|
912
917
|
|
|
@@ -956,7 +961,9 @@ def destroy(
|
|
|
956
961
|
color = "cyan"
|
|
957
962
|
else:
|
|
958
963
|
if result.errors:
|
|
959
|
-
console.print(
|
|
964
|
+
console.print(
|
|
965
|
+
f"[yellow]⚠️ Destruction completed with errors for agent '{result.agent_name}'[/yellow]\n"
|
|
966
|
+
)
|
|
960
967
|
title = "Destruction Results (With Errors)"
|
|
961
968
|
color = "yellow"
|
|
962
969
|
else:
|
|
@@ -987,7 +994,7 @@ def destroy(
|
|
|
987
994
|
console.print(" • Run 'agentcore configure --entrypoint <file>' to set up a new agent")
|
|
988
995
|
console.print(" • Run 'agentcore launch' to deploy to Bedrock AgentCore")
|
|
989
996
|
elif dry_run:
|
|
990
|
-
console.print(
|
|
997
|
+
console.print("\n[dim]To actually destroy these resources, run:[/dim]")
|
|
991
998
|
destroy_cmd = f" agentcore destroy{f' --agent {actual_agent_name}' if agent else ''}"
|
|
992
999
|
if delete_ecr_repo:
|
|
993
1000
|
destroy_cmd += " --delete-ecr-repo"
|
|
@@ -39,14 +39,18 @@ def destroy_bedrock_agentcore(
|
|
|
39
39
|
ValueError: If agent is not found or not deployed
|
|
40
40
|
RuntimeError: If destruction fails
|
|
41
41
|
"""
|
|
42
|
-
log.info(
|
|
43
|
-
|
|
42
|
+
log.info(
|
|
43
|
+
"Starting destroy operation for agent: %s (dry_run=%s, delete_ecr_repo=%s)",
|
|
44
|
+
agent_name or "default",
|
|
45
|
+
dry_run,
|
|
46
|
+
delete_ecr_repo,
|
|
47
|
+
)
|
|
44
48
|
|
|
45
49
|
try:
|
|
46
50
|
# Load configuration
|
|
47
51
|
project_config = load_config(config_path)
|
|
48
52
|
agent_config = project_config.get_agent_config(agent_name)
|
|
49
|
-
|
|
53
|
+
|
|
50
54
|
if not agent_config:
|
|
51
55
|
raise ValueError(f"Agent '{agent_name or 'default'}' not found in configuration")
|
|
52
56
|
|
|
@@ -60,32 +64,36 @@ def destroy_bedrock_agentcore(
|
|
|
60
64
|
|
|
61
65
|
# Initialize AWS session and clients
|
|
62
66
|
session = boto3.Session(region_name=agent_config.aws.region)
|
|
63
|
-
|
|
67
|
+
|
|
64
68
|
# 1. Destroy Bedrock AgentCore endpoint (if exists)
|
|
65
69
|
_destroy_agentcore_endpoint(session, agent_config, result, dry_run)
|
|
66
|
-
|
|
70
|
+
|
|
67
71
|
# 2. Destroy Bedrock AgentCore agent
|
|
68
72
|
_destroy_agentcore_agent(session, agent_config, result, dry_run)
|
|
69
|
-
|
|
73
|
+
|
|
70
74
|
# 3. Remove ECR images and optionally the repository
|
|
71
75
|
_destroy_ecr_images(session, agent_config, result, dry_run, delete_ecr_repo)
|
|
72
|
-
|
|
76
|
+
|
|
73
77
|
# 4. Remove CodeBuild project
|
|
74
78
|
_destroy_codebuild_project(session, agent_config, result, dry_run)
|
|
75
79
|
|
|
76
80
|
# 5. Remove CodeBuild IAM Role
|
|
77
81
|
_destroy_codebuild_iam_role(session, agent_config, result, dry_run)
|
|
78
|
-
|
|
82
|
+
|
|
79
83
|
# 6. Remove IAM execution role (if not used by other agents)
|
|
80
84
|
_destroy_iam_role(session, project_config, agent_config, result, dry_run)
|
|
81
|
-
|
|
85
|
+
|
|
82
86
|
# 7. Clean up configuration
|
|
83
87
|
if not dry_run and not result.errors:
|
|
84
88
|
_cleanup_agent_config(config_path, project_config, agent_config.name, result)
|
|
85
89
|
|
|
86
|
-
log.info(
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
log.info(
|
|
91
|
+
"Destroy operation completed. Resources removed: %d, Warnings: %d, Errors: %d",
|
|
92
|
+
len(result.resources_removed),
|
|
93
|
+
len(result.warnings),
|
|
94
|
+
len(result.errors),
|
|
95
|
+
)
|
|
96
|
+
|
|
89
97
|
return result
|
|
90
98
|
|
|
91
99
|
except Exception as e:
|
|
@@ -105,7 +113,7 @@ def _destroy_agentcore_endpoint(
|
|
|
105
113
|
|
|
106
114
|
try:
|
|
107
115
|
client = BedrockAgentCoreClient(agent_config.aws.region)
|
|
108
|
-
|
|
116
|
+
|
|
109
117
|
agent_id = agent_config.bedrock_agentcore.agent_id
|
|
110
118
|
if not agent_id:
|
|
111
119
|
result.warnings.append("No agent ID found, skipping endpoint destruction")
|
|
@@ -116,12 +124,10 @@ def _destroy_agentcore_endpoint(
|
|
|
116
124
|
endpoint_response = client.get_agent_runtime_endpoint(agent_id)
|
|
117
125
|
endpoint_name = endpoint_response.get("name", "DEFAULT")
|
|
118
126
|
endpoint_arn = endpoint_response.get("agentRuntimeEndpointArn")
|
|
119
|
-
|
|
127
|
+
|
|
120
128
|
# Special case: DEFAULT endpoint cannot be explicitly deleted
|
|
121
129
|
if endpoint_name == "DEFAULT":
|
|
122
|
-
result.warnings.append(
|
|
123
|
-
"DEFAULT endpoint cannot be explicitly deleted, skipping"
|
|
124
|
-
)
|
|
130
|
+
result.warnings.append("DEFAULT endpoint cannot be explicitly deleted, skipping")
|
|
125
131
|
log.info("Skipping deletion of DEFAULT endpoint")
|
|
126
132
|
return
|
|
127
133
|
|
|
@@ -143,7 +149,7 @@ def _destroy_agentcore_endpoint(
|
|
|
143
149
|
result.warnings.append("Endpoint not found or already deleted during deletion")
|
|
144
150
|
else:
|
|
145
151
|
result.warnings.append("No endpoint ARN found for agent")
|
|
146
|
-
|
|
152
|
+
|
|
147
153
|
except ClientError as e:
|
|
148
154
|
if e.response["Error"]["Code"] not in ["ResourceNotFoundException", "NotFound"]:
|
|
149
155
|
result.warnings.append(f"Failed to get endpoint info: {e}")
|
|
@@ -168,7 +174,8 @@ def _destroy_agentcore_agent(
|
|
|
168
174
|
return
|
|
169
175
|
|
|
170
176
|
try:
|
|
171
|
-
client
|
|
177
|
+
# Initialize client to enable exception handling path for tests
|
|
178
|
+
BedrockAgentCoreClient(agent_config.aws.region)
|
|
172
179
|
agent_arn = agent_config.bedrock_agentcore.agent_arn
|
|
173
180
|
agent_id = agent_config.bedrock_agentcore.agent_id
|
|
174
181
|
|
|
@@ -212,18 +219,18 @@ def _destroy_ecr_images(
|
|
|
212
219
|
# Create ECR client with explicit region specification
|
|
213
220
|
ecr_client = session.client("ecr", region_name=agent_config.aws.region)
|
|
214
221
|
ecr_uri = agent_config.aws.ecr_repository
|
|
215
|
-
|
|
222
|
+
|
|
216
223
|
# Extract repository name from URI
|
|
217
224
|
# Format: account.dkr.ecr.region.amazonaws.com/repo-name
|
|
218
225
|
repo_name = ecr_uri.split("/")[-1]
|
|
219
|
-
|
|
226
|
+
|
|
220
227
|
log.info("Checking ECR repository: %s in region: %s", repo_name, agent_config.aws.region)
|
|
221
228
|
|
|
222
229
|
try:
|
|
223
230
|
# List all images in the repository (both tagged and untagged)
|
|
224
231
|
response = ecr_client.list_images(repositoryName=repo_name)
|
|
225
232
|
log.debug("ECR list_images response: %s", response)
|
|
226
|
-
|
|
233
|
+
|
|
227
234
|
# Fix: use correct response key 'imageIds' instead of 'imageDetails'
|
|
228
235
|
all_images = response.get("imageIds", [])
|
|
229
236
|
if not all_images:
|
|
@@ -238,7 +245,7 @@ def _destroy_ecr_images(
|
|
|
238
245
|
return
|
|
239
246
|
|
|
240
247
|
if dry_run:
|
|
241
|
-
# Fix: imageIds structure has imageTag (string) not imageTags (array)
|
|
248
|
+
# Fix: imageIds structure has imageTag (string) not imageTags (array)
|
|
242
249
|
tagged_count = len([img for img in all_images if img.get("imageTag")])
|
|
243
250
|
untagged_count = len([img for img in all_images if not img.get("imageTag")])
|
|
244
251
|
result.resources_removed.append(
|
|
@@ -250,18 +257,18 @@ def _destroy_ecr_images(
|
|
|
250
257
|
|
|
251
258
|
# Prepare images for deletion - imageIds are already in the correct format
|
|
252
259
|
images_to_delete = []
|
|
253
|
-
|
|
260
|
+
|
|
254
261
|
for image in all_images:
|
|
255
262
|
# imageIds structure already contains the correct identifiers
|
|
256
263
|
image_id = {}
|
|
257
|
-
|
|
264
|
+
|
|
258
265
|
# If image has a tag, use it
|
|
259
266
|
if image.get("imageTag"):
|
|
260
267
|
image_id["imageTag"] = image["imageTag"]
|
|
261
|
-
# If no tag, use image digest
|
|
268
|
+
# If no tag, use image digest
|
|
262
269
|
elif image.get("imageDigest"):
|
|
263
270
|
image_id["imageDigest"] = image["imageDigest"]
|
|
264
|
-
|
|
271
|
+
|
|
265
272
|
if image_id:
|
|
266
273
|
images_to_delete.append(image_id)
|
|
267
274
|
|
|
@@ -269,34 +276,32 @@ def _destroy_ecr_images(
|
|
|
269
276
|
# Delete images in batches (ECR has a limit of 100 images per batch)
|
|
270
277
|
batch_size = 100
|
|
271
278
|
total_deleted = 0
|
|
272
|
-
|
|
279
|
+
|
|
273
280
|
for i in range(0, len(images_to_delete), batch_size):
|
|
274
|
-
batch = images_to_delete[i:i + batch_size]
|
|
275
|
-
|
|
276
|
-
delete_response = ecr_client.batch_delete_image(
|
|
277
|
-
|
|
278
|
-
imageIds=batch
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
+
batch = images_to_delete[i : i + batch_size]
|
|
282
|
+
|
|
283
|
+
delete_response = ecr_client.batch_delete_image(repositoryName=repo_name, imageIds=batch)
|
|
284
|
+
|
|
281
285
|
deleted_images = delete_response.get("imageIds", [])
|
|
282
286
|
total_deleted += len(deleted_images)
|
|
283
|
-
|
|
287
|
+
|
|
284
288
|
# Log any failures in this batch
|
|
285
289
|
failures = delete_response.get("failures", [])
|
|
286
290
|
for failure in failures:
|
|
287
|
-
log.warning(
|
|
288
|
-
|
|
291
|
+
log.warning(
|
|
292
|
+
"Failed to delete image: %s - %s", failure.get("imageId"), failure.get("failureReason")
|
|
293
|
+
)
|
|
289
294
|
|
|
290
295
|
result.resources_removed.append(f"ECR images: {total_deleted} images from {repo_name}")
|
|
291
296
|
log.info("Deleted %d ECR images from %s", total_deleted, repo_name)
|
|
292
|
-
|
|
297
|
+
|
|
293
298
|
# Log any partial failures
|
|
294
299
|
if total_deleted < len(images_to_delete):
|
|
295
300
|
failed_count = len(images_to_delete) - total_deleted
|
|
296
301
|
result.warnings.append(
|
|
297
302
|
f"Some ECR images could not be deleted: {failed_count} out of {len(images_to_delete)} failed"
|
|
298
303
|
)
|
|
299
|
-
|
|
304
|
+
|
|
300
305
|
# Delete the repository if requested and all images were deleted successfully
|
|
301
306
|
if delete_ecr_repo and total_deleted == len(images_to_delete):
|
|
302
307
|
_delete_ecr_repository(ecr_client, repo_name, result)
|
|
@@ -326,16 +331,16 @@ def _delete_ecr_repository(ecr_client, repo_name: str, result: DestroyResult) ->
|
|
|
326
331
|
# Verify repository is empty before deletion
|
|
327
332
|
response = ecr_client.list_images(repositoryName=repo_name)
|
|
328
333
|
remaining_images = response.get("imageIds", [])
|
|
329
|
-
|
|
334
|
+
|
|
330
335
|
if remaining_images:
|
|
331
336
|
result.warnings.append(f"Cannot delete ECR repository {repo_name}: repository is not empty")
|
|
332
337
|
return
|
|
333
|
-
|
|
338
|
+
|
|
334
339
|
# Delete the empty repository
|
|
335
340
|
ecr_client.delete_repository(repositoryName=repo_name)
|
|
336
341
|
result.resources_removed.append(f"ECR repository: {repo_name}")
|
|
337
342
|
log.info("Deleted ECR repository: %s", repo_name)
|
|
338
|
-
|
|
343
|
+
|
|
339
344
|
except ClientError as e:
|
|
340
345
|
error_code = e.response["Error"]["Code"]
|
|
341
346
|
if error_code == "RepositoryNotFoundException":
|
|
@@ -380,6 +385,7 @@ def _destroy_codebuild_project(
|
|
|
380
385
|
result.warnings.append(f"Error during CodeBuild cleanup: {e}")
|
|
381
386
|
log.warning("Error during CodeBuild cleanup: %s", e)
|
|
382
387
|
|
|
388
|
+
|
|
383
389
|
def _destroy_codebuild_iam_role(
|
|
384
390
|
session: boto3.Session,
|
|
385
391
|
agent_config: BedrockAgentCoreAgentSchema,
|
|
@@ -390,17 +396,17 @@ def _destroy_codebuild_iam_role(
|
|
|
390
396
|
if not agent_config.codebuild.execution_role:
|
|
391
397
|
result.warnings.append("No CodeBuild execution role configured, skipping IAM cleanup")
|
|
392
398
|
return
|
|
393
|
-
|
|
399
|
+
|
|
394
400
|
try:
|
|
395
401
|
# Note: IAM is a global service, but we specify region for consistency
|
|
396
402
|
iam_client = session.client("iam", region_name=agent_config.aws.region)
|
|
397
403
|
role_arn = agent_config.codebuild.execution_role
|
|
398
404
|
role_name = role_arn.split("/")[-1]
|
|
399
|
-
|
|
405
|
+
|
|
400
406
|
if dry_run:
|
|
401
407
|
result.resources_removed.append(f"CodeBuild IAM role: {role_name} (DRY RUN)")
|
|
402
408
|
return
|
|
403
|
-
|
|
409
|
+
|
|
404
410
|
# Detach managed policies
|
|
405
411
|
for policy in iam_client.list_attached_role_policies(RoleName=role_name).get("AttachedPolicies", []):
|
|
406
412
|
iam_client.detach_role_policy(RoleName=role_name, PolicyArn=policy["PolicyArn"])
|
|
@@ -415,7 +421,7 @@ def _destroy_codebuild_iam_role(
|
|
|
415
421
|
iam_client.delete_role(RoleName=role_name)
|
|
416
422
|
result.resources_removed.append(f"Deleted CodeBuild IAM role: {role_name}")
|
|
417
423
|
log.info("Deleted CodeBuild IAM role: %s", role_name)
|
|
418
|
-
|
|
424
|
+
|
|
419
425
|
except ClientError as e:
|
|
420
426
|
result.warnings.append(f"Failed to delete CodeBuild role {role_name}: {e}")
|
|
421
427
|
log.warning("Failed to delete CodeBuild role %s: %s", role_name, e)
|
|
@@ -423,6 +429,7 @@ def _destroy_codebuild_iam_role(
|
|
|
423
429
|
result.warnings.append(f"Error during CodeBuild IAM role cleanup: {e}")
|
|
424
430
|
log.error("Error during CodeBuild IAM role cleanup: %s", e)
|
|
425
431
|
|
|
432
|
+
|
|
426
433
|
def _destroy_iam_role(
|
|
427
434
|
session: boto3.Session,
|
|
428
435
|
project_config: BedrockAgentCoreConfigSchema,
|
|
@@ -443,7 +450,8 @@ def _destroy_iam_role(
|
|
|
443
450
|
|
|
444
451
|
# Check if other agents use the same role
|
|
445
452
|
other_agents_using_role = [
|
|
446
|
-
name
|
|
453
|
+
name
|
|
454
|
+
for name, agent in project_config.agents.items()
|
|
447
455
|
if name != agent_config.name and agent.aws.execution_role == role_arn
|
|
448
456
|
]
|
|
449
457
|
|
|
@@ -462,10 +470,7 @@ def _destroy_iam_role(
|
|
|
462
470
|
try:
|
|
463
471
|
policies = iam_client.list_attached_role_policies(RoleName=role_name)
|
|
464
472
|
for policy in policies.get("AttachedPolicies", []):
|
|
465
|
-
iam_client.detach_role_policy(
|
|
466
|
-
RoleName=role_name,
|
|
467
|
-
PolicyArn=policy["PolicyArn"]
|
|
468
|
-
)
|
|
473
|
+
iam_client.detach_role_policy(RoleName=role_name, PolicyArn=policy["PolicyArn"])
|
|
469
474
|
except ClientError:
|
|
470
475
|
pass # Continue if policy detachment fails
|
|
471
476
|
|
|
@@ -508,12 +513,12 @@ def _cleanup_agent_config(
|
|
|
508
513
|
|
|
509
514
|
# Check if this agent is the default agent
|
|
510
515
|
was_default = project_config.default_agent == agent_name
|
|
511
|
-
|
|
516
|
+
|
|
512
517
|
# Remove the agent entry completely
|
|
513
518
|
del project_config.agents[agent_name]
|
|
514
519
|
result.resources_removed.append(f"Agent configuration: {agent_name}")
|
|
515
520
|
log.info("Removed agent configuration: %s", agent_name)
|
|
516
|
-
|
|
521
|
+
|
|
517
522
|
# Handle default agent cleanup
|
|
518
523
|
if was_default:
|
|
519
524
|
if project_config.agents:
|
|
@@ -526,7 +531,7 @@ def _cleanup_agent_config(
|
|
|
526
531
|
# No agents left, clear default
|
|
527
532
|
project_config.default_agent = None
|
|
528
533
|
log.info("Cleared default agent (no agents remaining)")
|
|
529
|
-
|
|
534
|
+
|
|
530
535
|
# If no agents remain, remove the config file
|
|
531
536
|
if not project_config.agents:
|
|
532
537
|
config_path.unlink()
|
|
@@ -539,4 +544,4 @@ def _cleanup_agent_config(
|
|
|
539
544
|
|
|
540
545
|
except Exception as e:
|
|
541
546
|
result.warnings.append(f"Failed to update configuration: {e}")
|
|
542
|
-
log.warning("Failed to update configuration: %s", e)
|
|
547
|
+
log.warning("Failed to update configuration: %s", e)
|
|
@@ -67,7 +67,7 @@ def invoke_bedrock_agentcore(
|
|
|
67
67
|
)["workloadAccessToken"]
|
|
68
68
|
|
|
69
69
|
# TODO: store and read port config of local running container
|
|
70
|
-
client = LocalBedrockAgentCoreClient("http://
|
|
70
|
+
client = LocalBedrockAgentCoreClient("http://127.0.0.1:8080")
|
|
71
71
|
response = client.invoke_endpoint(session_id, payload_str, workload_access_token)
|
|
72
72
|
|
|
73
73
|
else:
|
|
@@ -13,8 +13,10 @@ from botocore.exceptions import ClientError
|
|
|
13
13
|
from ...services.codebuild import CodeBuildService
|
|
14
14
|
from ...services.ecr import deploy_to_ecr, get_or_create_ecr_repository
|
|
15
15
|
from ...services.runtime import BedrockAgentCoreClient
|
|
16
|
+
from ...services.xray import enable_transaction_search_if_needed
|
|
16
17
|
from ...utils.runtime.config import load_config, save_config
|
|
17
18
|
from ...utils.runtime.container import ContainerRuntime
|
|
19
|
+
from ...utils.runtime.logs import get_genai_observability_url
|
|
18
20
|
from ...utils.runtime.schema import BedrockAgentCoreAgentSchema, BedrockAgentCoreConfigSchema
|
|
19
21
|
from .create_role import get_or_create_runtime_execution_role
|
|
20
22
|
from .models import LaunchResult
|
|
@@ -135,6 +137,7 @@ def _deploy_to_bedrock_agentcore(
|
|
|
135
137
|
agent_name: str,
|
|
136
138
|
ecr_uri: str,
|
|
137
139
|
region: str,
|
|
140
|
+
account_id: str,
|
|
138
141
|
env_vars: Optional[dict] = None,
|
|
139
142
|
auto_update_on_conflict: bool = False,
|
|
140
143
|
):
|
|
@@ -230,6 +233,16 @@ def _deploy_to_bedrock_agentcore(
|
|
|
230
233
|
|
|
231
234
|
log.info("✅ Agent created/updated: %s", agent_arn)
|
|
232
235
|
|
|
236
|
+
# Enable Transaction Search if observability is enabled
|
|
237
|
+
if agent_config.aws.observability.enabled:
|
|
238
|
+
log.info("Observability is enabled, configuring Transaction Search...")
|
|
239
|
+
enable_transaction_search_if_needed(region, account_id)
|
|
240
|
+
|
|
241
|
+
# Show GenAI Observability Dashboard URL whenever OTEL is enabled
|
|
242
|
+
console_url = get_genai_observability_url(region)
|
|
243
|
+
log.info("🔍 GenAI Observability Dashboard:")
|
|
244
|
+
log.info(" %s", console_url)
|
|
245
|
+
|
|
233
246
|
# Wait for agent to be ready
|
|
234
247
|
log.info("Polling for endpoint to be ready...")
|
|
235
248
|
result = bedrock_agentcore_client.wait_for_agent_endpoint_ready(agent_id)
|
|
@@ -367,6 +380,7 @@ def launch_bedrock_agentcore(
|
|
|
367
380
|
bedrock_agentcore_name,
|
|
368
381
|
ecr_uri,
|
|
369
382
|
region,
|
|
383
|
+
account_id,
|
|
370
384
|
env_vars,
|
|
371
385
|
auto_update_on_conflict,
|
|
372
386
|
)
|
|
@@ -495,6 +509,7 @@ def _launch_with_codebuild(
|
|
|
495
509
|
agent_name,
|
|
496
510
|
ecr_uri,
|
|
497
511
|
region,
|
|
512
|
+
account_id,
|
|
498
513
|
env_vars=env_vars,
|
|
499
514
|
auto_update_on_conflict=auto_update_on_conflict,
|
|
500
515
|
)
|
|
@@ -21,6 +21,7 @@ from openapi_schema_to_json_schema import to_json_schema
|
|
|
21
21
|
|
|
22
22
|
from ....operations.gateway import GatewayClient
|
|
23
23
|
from ..utils import (
|
|
24
|
+
clean_gateway_or_target_name,
|
|
24
25
|
clean_variable_name,
|
|
25
26
|
generate_pydantic_models,
|
|
26
27
|
get_base_dir,
|
|
@@ -1064,7 +1065,7 @@ class BaseBedrockTranslator:
|
|
|
1064
1065
|
)
|
|
1065
1066
|
response_content_code = "str(agent_result)" if platform == "strands" else "agent_result[-1].content"
|
|
1066
1067
|
url_pattern = self._get_url_regex_pattern()
|
|
1067
|
-
|
|
1068
|
+
|
|
1068
1069
|
entrypoint_code += f"""
|
|
1069
1070
|
def endpoint(payload, context):
|
|
1070
1071
|
try:
|
|
@@ -1179,7 +1180,7 @@ class BaseBedrockTranslator:
|
|
|
1179
1180
|
continue
|
|
1180
1181
|
|
|
1181
1182
|
action_group_name = ag.get("actionGroupName", "AG")
|
|
1182
|
-
clean_action_group_name =
|
|
1183
|
+
clean_action_group_name = clean_gateway_or_target_name(action_group_name)
|
|
1183
1184
|
action_group_desc = ag.get("description", "").replace('"', '\\"')
|
|
1184
1185
|
end_lambda_arn = ag.get("actionGroupExecutor", {}).get("lambda", "")
|
|
1185
1186
|
tools = []
|
|
@@ -1323,7 +1324,7 @@ class BaseBedrockTranslator:
|
|
|
1323
1324
|
"lambdaRegion": end_lambda_arn.split(":")[3] if end_lambda_arn else "us-west-2",
|
|
1324
1325
|
}
|
|
1325
1326
|
|
|
1326
|
-
func_desc =
|
|
1327
|
+
func_desc = func.get("description", "No Description Provided.")
|
|
1327
1328
|
func_desc += f"\\nThis tool is part of the group of tools called {action_group_name}{f' (description: {action_group_desc})' if action_group_desc else ''}."
|
|
1328
1329
|
|
|
1329
1330
|
func_parameters = func.get("parameters", {})
|
|
@@ -50,6 +50,20 @@ def clean_variable_name(text):
|
|
|
50
50
|
return cleaned
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
def clean_gateway_or_target_name(text):
|
|
54
|
+
"""Clean a string to create a valid Gateway or Target name."""
|
|
55
|
+
text = str(text)
|
|
56
|
+
cleaned = re.sub(r"[^a-zA-Z0-9\s]", " ", text)
|
|
57
|
+
cleaned = cleaned.lower()
|
|
58
|
+
cleaned = re.sub(r"\s+", " ", cleaned)
|
|
59
|
+
cleaned = cleaned.strip()
|
|
60
|
+
cleaned = cleaned.replace(" ", "-")
|
|
61
|
+
if not cleaned:
|
|
62
|
+
cleaned = "gateway-or-target"
|
|
63
|
+
|
|
64
|
+
return cleaned
|
|
65
|
+
|
|
66
|
+
|
|
53
67
|
def unindent_by_one(input_code, spaces_per_indent=4):
|
|
54
68
|
"""Unindents the input code by one level of indentation.
|
|
55
69
|
|
|
@@ -399,7 +399,11 @@ class BedrockAgentCoreClient:
|
|
|
399
399
|
agentRuntimeId=agent_id,
|
|
400
400
|
endpointName=endpoint_name,
|
|
401
401
|
)
|
|
402
|
-
self.logger.info(
|
|
402
|
+
self.logger.info(
|
|
403
|
+
"Successfully initiated deletion of endpoint '%s' for agent ID: %s",
|
|
404
|
+
endpoint_name,
|
|
405
|
+
agent_id,
|
|
406
|
+
)
|
|
403
407
|
return response
|
|
404
408
|
except Exception as e:
|
|
405
409
|
self.logger.error("Failed to delete endpoint '%s' for agent ID '%s': %s", endpoint_name, agent_id, str(e))
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""X-Ray Transaction Search service for enabling observability."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import boto3
|
|
7
|
+
from botocore.exceptions import ClientError
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _need_resource_policy(logs_client, policy_name="TransactionSearchXRayAccess"):
|
|
13
|
+
"""Check if resource policy needs to be created (fail-safe)."""
|
|
14
|
+
try:
|
|
15
|
+
response = logs_client.describe_resource_policies()
|
|
16
|
+
for policy in response.get("resourcePolicies", []):
|
|
17
|
+
if policy.get("policyName") == policy_name:
|
|
18
|
+
return False # Already exists
|
|
19
|
+
return True # Needs creation
|
|
20
|
+
except Exception:
|
|
21
|
+
return True # If check fails, assume we need it (safe)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _need_trace_destination(xray_client):
|
|
25
|
+
"""Check if trace destination needs to be set (fail-safe)."""
|
|
26
|
+
try:
|
|
27
|
+
response = xray_client.get_trace_segment_destination()
|
|
28
|
+
return response.get("Destination") != "CloudWatchLogs"
|
|
29
|
+
except Exception:
|
|
30
|
+
return True # If check fails, assume we need it (safe)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _need_indexing_rule(xray_client):
|
|
34
|
+
"""Check if indexing rule needs to be configured (fail-safe)."""
|
|
35
|
+
try:
|
|
36
|
+
response = xray_client.get_indexing_rules()
|
|
37
|
+
for rule in response.get("IndexingRules", []):
|
|
38
|
+
if rule.get("Name") == "Default":
|
|
39
|
+
return False # Already configured
|
|
40
|
+
return True # Needs configuration
|
|
41
|
+
except Exception:
|
|
42
|
+
return True # If check fails, assume we need it (safe)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def enable_transaction_search_if_needed(region: str, account_id: str) -> bool:
|
|
46
|
+
"""Enable X-Ray Transaction Search components that are not already configured.
|
|
47
|
+
|
|
48
|
+
This function checks what's already configured and only runs needed steps.
|
|
49
|
+
It's fail-safe - if checks fail, it assumes configuration is needed.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
region: AWS region
|
|
53
|
+
account_id: AWS account ID
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
bool: True if Transaction Search was configured successfully, False if failed
|
|
57
|
+
"""
|
|
58
|
+
try:
|
|
59
|
+
session = boto3.Session(region_name=region)
|
|
60
|
+
logs_client = session.client("logs")
|
|
61
|
+
xray_client = session.client("xray")
|
|
62
|
+
|
|
63
|
+
steps_run = []
|
|
64
|
+
|
|
65
|
+
# Step 1: Resource policy (only if needed)
|
|
66
|
+
if _need_resource_policy(logs_client):
|
|
67
|
+
_create_cloudwatch_logs_resource_policy(logs_client, account_id, region)
|
|
68
|
+
steps_run.append("resource_policy")
|
|
69
|
+
else:
|
|
70
|
+
logger.info("CloudWatch Logs resource policy already configured")
|
|
71
|
+
|
|
72
|
+
# Step 2: Trace destination (only if needed)
|
|
73
|
+
if _need_trace_destination(xray_client):
|
|
74
|
+
_configure_trace_segment_destination(xray_client)
|
|
75
|
+
steps_run.append("trace_destination")
|
|
76
|
+
else:
|
|
77
|
+
logger.info("X-Ray trace destination already configured")
|
|
78
|
+
|
|
79
|
+
# Step 3: Indexing rule (only if needed)
|
|
80
|
+
if _need_indexing_rule(xray_client):
|
|
81
|
+
_configure_indexing_rule(xray_client)
|
|
82
|
+
steps_run.append("indexing_rule")
|
|
83
|
+
else:
|
|
84
|
+
logger.info("X-Ray indexing rule already configured")
|
|
85
|
+
|
|
86
|
+
if steps_run:
|
|
87
|
+
logger.info("✅ Transaction Search configured: %s", ", ".join(steps_run))
|
|
88
|
+
else:
|
|
89
|
+
logger.info("✅ Transaction Search already fully configured")
|
|
90
|
+
|
|
91
|
+
return True
|
|
92
|
+
|
|
93
|
+
except Exception as e:
|
|
94
|
+
logger.warning("Transaction Search configuration failed: %s", str(e))
|
|
95
|
+
logger.info("Agent launch will continue without Transaction Search")
|
|
96
|
+
return False # Don't fail launch
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _create_cloudwatch_logs_resource_policy(logs_client, account_id: str, region: str) -> None:
|
|
100
|
+
"""Create CloudWatch Logs resource policy for X-Ray access (idempotent)."""
|
|
101
|
+
policy_name = "TransactionSearchXRayAccess"
|
|
102
|
+
|
|
103
|
+
policy_document = {
|
|
104
|
+
"Version": "2012-10-17",
|
|
105
|
+
"Statement": [
|
|
106
|
+
{
|
|
107
|
+
"Sid": "TransactionSearchXRayAccess",
|
|
108
|
+
"Effect": "Allow",
|
|
109
|
+
"Principal": {"Service": "xray.amazonaws.com"},
|
|
110
|
+
"Action": "logs:PutLogEvents",
|
|
111
|
+
"Resource": [
|
|
112
|
+
f"arn:aws:logs:{region}:{account_id}:log-group:aws/spans:*",
|
|
113
|
+
f"arn:aws:logs:{region}:{account_id}:log-group:/aws/application-signals/data:*",
|
|
114
|
+
],
|
|
115
|
+
"Condition": {
|
|
116
|
+
"ArnLike": {"aws:SourceArn": f"arn:aws:xray:{region}:{account_id}:*"},
|
|
117
|
+
"StringEquals": {"aws:SourceAccount": account_id},
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
logs_client.put_resource_policy(policyName=policy_name, policyDocument=json.dumps(policy_document))
|
|
125
|
+
logger.info("Created/updated CloudWatch Logs resource policy")
|
|
126
|
+
except ClientError as e:
|
|
127
|
+
if e.response["Error"]["Code"] == "InvalidParameterException":
|
|
128
|
+
# Policy might already exist with same content
|
|
129
|
+
logger.info("CloudWatch Logs resource policy already configured")
|
|
130
|
+
else:
|
|
131
|
+
raise
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _configure_trace_segment_destination(xray_client) -> None:
|
|
135
|
+
"""Configure X-Ray trace segment destination to CloudWatch Logs (idempotent)."""
|
|
136
|
+
try:
|
|
137
|
+
# Configure trace segments to be sent to CloudWatch Logs
|
|
138
|
+
# This enables Transaction Search functionality
|
|
139
|
+
xray_client.update_trace_segment_destination(Destination="CloudWatchLogs")
|
|
140
|
+
logger.info("Configured X-Ray trace segment destination to CloudWatch Logs")
|
|
141
|
+
except ClientError as e:
|
|
142
|
+
if e.response["Error"]["Code"] == "InvalidRequestException":
|
|
143
|
+
# Destination might already be configured
|
|
144
|
+
logger.info("X-Ray trace segment destination already configured")
|
|
145
|
+
else:
|
|
146
|
+
raise
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _configure_indexing_rule(xray_client) -> None:
|
|
150
|
+
"""Configure X-Ray indexing rule for transaction search (idempotent)."""
|
|
151
|
+
try:
|
|
152
|
+
# Update the default indexing rule with probabilistic sampling
|
|
153
|
+
# This is idempotent - it will update the existing rule
|
|
154
|
+
xray_client.update_indexing_rule(Name="Default", Rule={"Probabilistic": {"DesiredSamplingPercentage": 1}})
|
|
155
|
+
logger.info("Updated X-Ray indexing rule for Transaction Search")
|
|
156
|
+
except ClientError as e:
|
|
157
|
+
if e.response["Error"]["Code"] == "InvalidRequestException":
|
|
158
|
+
# Rule might already be configured
|
|
159
|
+
logger.info("X-Ray indexing rule already configured")
|
|
160
|
+
else:
|
|
161
|
+
raise
|
|
@@ -4,6 +4,18 @@ from datetime import datetime, timezone
|
|
|
4
4
|
from typing import Optional, Tuple
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
def get_genai_observability_url(region: str) -> str:
|
|
8
|
+
"""Get GenAI Observability Dashboard console URL.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
region: The AWS region
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
The GenAI Observability Dashboard console URL
|
|
15
|
+
"""
|
|
16
|
+
return f"https://console.aws.amazon.com/cloudwatch/home?region={region}#gen-ai-observability/agent-core"
|
|
17
|
+
|
|
18
|
+
|
|
7
19
|
def get_agent_log_paths(agent_id: str, endpoint_name: Optional[str] = None) -> Tuple[str, str]:
|
|
8
20
|
"""Get CloudWatch log group paths for an agent.
|
|
9
21
|
|
|
@@ -1,43 +1,29 @@
|
|
|
1
1
|
FROM ghcr.io/astral-sh/uv:python{{ python_version }}-bookworm-slim
|
|
2
|
-
WORKDIR /app
|
|
3
2
|
|
|
4
|
-
#
|
|
5
|
-
ENV UV_SYSTEM_PYTHON=1 UV_COMPILE_BYTECODE=1
|
|
3
|
+
# All environment variables in one layer
|
|
4
|
+
ENV UV_SYSTEM_PYTHON=1 UV_COMPILE_BYTECODE=1 PYTHONUNBUFFERED=1 \
|
|
5
|
+
{% if aws_region %} AWS_REGION={{ aws_region }} AWS_DEFAULT_REGION={{ aws_region }} \
|
|
6
|
+
{% endif %} DOCKER_CONTAINER=1
|
|
6
7
|
|
|
7
8
|
{% if dependencies_file %}
|
|
8
9
|
{% if dependencies_install_path %}
|
|
9
10
|
COPY {{ dependencies_install_path }} {{ dependencies_install_path }}
|
|
10
|
-
# Install from pyproject.toml directory
|
|
11
|
-
RUN uv pip install {{ dependencies_install_path }}
|
|
12
11
|
{% else %}
|
|
13
12
|
COPY {{ dependencies_file }} {{ dependencies_file }}
|
|
14
|
-
# Install from requirements file
|
|
15
|
-
RUN uv pip install -r {{ dependencies_file }}
|
|
16
13
|
{% endif %}
|
|
17
14
|
{% endif %}
|
|
18
15
|
|
|
19
|
-
{% if observability_enabled %}
|
|
20
|
-
RUN uv pip install
|
|
21
|
-
{% endif %}
|
|
22
|
-
|
|
23
|
-
# Set AWS region environment variable
|
|
24
|
-
{% if aws_region %}
|
|
25
|
-
ENV AWS_REGION={{ aws_region }}
|
|
26
|
-
ENV AWS_DEFAULT_REGION={{ aws_region }}
|
|
16
|
+
{% if dependencies_file or observability_enabled %}
|
|
17
|
+
RUN {% if dependencies_file %}{% if dependencies_install_path %}uv pip install {{ dependencies_install_path }}{% else %}uv pip install -r {{ dependencies_file }}{% endif %}{% endif %}{% if observability_enabled %}{% if dependencies_file %} && \
|
|
18
|
+
{% endif %}uv pip install aws-opentelemetry-distro>=0.10.1{% endif %}
|
|
27
19
|
{% endif %}
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
ENV DOCKER_CONTAINER=1
|
|
21
|
+
EXPOSE 8080 8000
|
|
31
22
|
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
USER bedrock_agentcore
|
|
35
|
-
|
|
36
|
-
EXPOSE 8080
|
|
37
|
-
EXPOSE 8000
|
|
38
|
-
|
|
39
|
-
# Copy entire project (respecting .dockerignore)
|
|
23
|
+
# Copy entire project
|
|
24
|
+
{% if not dependencies_install_path or dependencies_install_path != '.' %}
|
|
40
25
|
COPY . .
|
|
26
|
+
{% endif %}
|
|
41
27
|
|
|
42
28
|
# Use the full module path
|
|
43
29
|
{% if observability_enabled %}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bedrock-agentcore-starter-toolkit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: A starter toolkit for using Bedrock AgentCore
|
|
5
5
|
Project-URL: Homepage, https://github.com/aws/bedrock-agentcore-starter-toolkit
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/aws/bedrock-agentcore-starter-toolkit/issues
|
|
@@ -22,7 +22,7 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
24
|
Requires-Dist: autopep8>=2.3.2
|
|
25
|
-
Requires-Dist: bedrock-agentcore>=0.1.
|
|
25
|
+
Requires-Dist: bedrock-agentcore>=0.1.4
|
|
26
26
|
Requires-Dist: boto3>=1.39.7
|
|
27
27
|
Requires-Dist: botocore>=1.39.7
|
|
28
28
|
Requires-Dist: docstring-parser<1.0,>=0.15
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
bedrock_agentcore_starter_toolkit/__init__.py,sha256=tN3-JWKvxk4ZSJQJIHQ4mMsDtt8J_cDCz-drcGU9USA,120
|
|
2
2
|
bedrock_agentcore_starter_toolkit/cli/__init__.py,sha256=WuIWfHtJKD9gQA-mE49bq8OHGb0Ugt8upRGaOp6-H90,62
|
|
3
|
-
bedrock_agentcore_starter_toolkit/cli/cli.py,sha256=
|
|
3
|
+
bedrock_agentcore_starter_toolkit/cli/cli.py,sha256=G910onhHZRGVwFITZXYY-x1A_C7hmERyqYrdgcmZcoI,1064
|
|
4
4
|
bedrock_agentcore_starter_toolkit/cli/common.py,sha256=R9ZRKmW9gq7u7gkTiCUOTCQTno2lXnsplnq2x0-k2v8,1311
|
|
5
5
|
bedrock_agentcore_starter_toolkit/cli/gateway/__init__.py,sha256=8IZ0kSe6Kz5s2j-SBsoc6qy04MbK85RMTQwZCiR2wmo,68
|
|
6
6
|
bedrock_agentcore_starter_toolkit/cli/gateway/commands.py,sha256=3DuXCvqXlmHU2cmjGzDruyc2Fkrpgoi158myj0vc3nA,3265
|
|
@@ -9,7 +9,7 @@ bedrock_agentcore_starter_toolkit/cli/import_agent/__init__.py,sha256=tyM3dXM3Tc
|
|
|
9
9
|
bedrock_agentcore_starter_toolkit/cli/import_agent/agent_info.py,sha256=V0fZEbV76_H3gmkA17yscyJ8UdcMAquzNqENQ9DXdHA,9477
|
|
10
10
|
bedrock_agentcore_starter_toolkit/cli/import_agent/commands.py,sha256=vpA66fGwKf6cxBelT3Yb_Af5Ow3AMquGLIDe-tBLjmc,22419
|
|
11
11
|
bedrock_agentcore_starter_toolkit/cli/runtime/__init__.py,sha256=0zKPPoYThoDIr3DZhIQJavq5nVTKRsgcE1s9--wx118,60
|
|
12
|
-
bedrock_agentcore_starter_toolkit/cli/runtime/commands.py,sha256=
|
|
12
|
+
bedrock_agentcore_starter_toolkit/cli/runtime/commands.py,sha256=s7-JKK5gEmY7amfXPcPmQGQv7487fU-mxdUfTOEz9KA,45283
|
|
13
13
|
bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py,sha256=5TJK80uzA1ARh263smLfthw1t5Ona3bAtdO1pE7OfNo,5808
|
|
14
14
|
bedrock_agentcore_starter_toolkit/notebook/__init__.py,sha256=wH1ZzIVKsKT_P0qX2kIIoyVxeHj8K40odfR1YI3McHw,129
|
|
15
15
|
bedrock_agentcore_starter_toolkit/notebook/runtime/__init__.py,sha256=DoGfB_uGFLANJVE9rSZtTH3ymw76WBWmD9vORvBIdXs,66
|
|
@@ -24,23 +24,24 @@ bedrock_agentcore_starter_toolkit/operations/gateway/exceptions.py,sha256=WjsrE7
|
|
|
24
24
|
bedrock_agentcore_starter_toolkit/operations/runtime/__init__.py,sha256=0jRUuwSoqh4R_WqzfP4XAXngrgyFK5uH8JGXUVars6Y,793
|
|
25
25
|
bedrock_agentcore_starter_toolkit/operations/runtime/configure.py,sha256=7xjNN6fn1U2cv20W2tmHdlbSjc-RU953tnbOaH5iXPQ,9056
|
|
26
26
|
bedrock_agentcore_starter_toolkit/operations/runtime/create_role.py,sha256=o3rimy-9SDOS0r-DKHctKSS6dAVIGelhn1zUhrSeolY,15952
|
|
27
|
-
bedrock_agentcore_starter_toolkit/operations/runtime/destroy.py,sha256=
|
|
28
|
-
bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py,sha256=
|
|
29
|
-
bedrock_agentcore_starter_toolkit/operations/runtime/launch.py,sha256=
|
|
27
|
+
bedrock_agentcore_starter_toolkit/operations/runtime/destroy.py,sha256=XuQQ_fBetQxanIZQx5MMvKYQGbaUHqU9JwJXZOeraco,23541
|
|
28
|
+
bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py,sha256=Cd3GwL0ft2z6nqSy-ESi94yI_anagLjrFV4uNLJTPaA,4562
|
|
29
|
+
bedrock_agentcore_starter_toolkit/operations/runtime/launch.py,sha256=NWiMHPnhz1LMFd5o9HV2OkezCsK6T52MK7PELuCEKtk,19928
|
|
30
30
|
bedrock_agentcore_starter_toolkit/operations/runtime/models.py,sha256=iSpD6Zc-FyS4Fu_oRzXzre4N9uYuzBMDkvZZx4MhBGE,4220
|
|
31
31
|
bedrock_agentcore_starter_toolkit/operations/runtime/status.py,sha256=tpOtzAq1S3z_7baxR_WOT8Avo3MtWKGpelVOOfb-uMA,2516
|
|
32
32
|
bedrock_agentcore_starter_toolkit/services/__init__.py,sha256=s7QtYYFCCX2ff0gZHUdDxCDI3zhUq0fPsjevZbF9xdA,66
|
|
33
33
|
bedrock_agentcore_starter_toolkit/services/codebuild.py,sha256=77aagVvjSMQxBfNmwYTO6lwE_2izXGqgc6kWp9Bf98U,13618
|
|
34
34
|
bedrock_agentcore_starter_toolkit/services/ecr.py,sha256=nW9wIZcXI6amZeLVSmM9F6awVBQP1-zrFXTozSNEDjo,2805
|
|
35
|
-
bedrock_agentcore_starter_toolkit/services/runtime.py,sha256=
|
|
35
|
+
bedrock_agentcore_starter_toolkit/services/runtime.py,sha256=azrGTGjQhQczEY_F3dwfsNeUx0n2Uc_9R9WAu4HOSrM,19984
|
|
36
|
+
bedrock_agentcore_starter_toolkit/services/xray.py,sha256=CRlcfVXpghVy7PvZqLUC4rp49Sw5DQ98rUU61HAluRM,6363
|
|
36
37
|
bedrock_agentcore_starter_toolkit/services/import_agent/__init__.py,sha256=ig-xanZqA3oJdRTucYz9xF9_2yi31ADRVIKFsnIw_l4,68
|
|
37
|
-
bedrock_agentcore_starter_toolkit/services/import_agent/utils.py,sha256=
|
|
38
|
+
bedrock_agentcore_starter_toolkit/services/import_agent/utils.py,sha256=yCnzqv3S8sbQi6ArTz3MeR4ggRcojbAgMbBb7KAf0sA,16077
|
|
38
39
|
bedrock_agentcore_starter_toolkit/services/import_agent/assets/memory_manager_template.py,sha256=RKrtTxir5XxyMg6cim4CAEbguaxb4mg1Qb31pd7D_kY,7892
|
|
39
40
|
bedrock_agentcore_starter_toolkit/services/import_agent/assets/requirements_langchain.j2,sha256=gZcpxIW0D4qSU6hNPSRZ75zBB6ES7IZj1G-mrfEJG1s,181
|
|
40
41
|
bedrock_agentcore_starter_toolkit/services/import_agent/assets/requirements_strands.j2,sha256=ZAWzCFwdj8Mbq8aBGO3Jnxz0G8XJZPNpIsPf_2Jetfk,100
|
|
41
42
|
bedrock_agentcore_starter_toolkit/services/import_agent/assets/template_fixtures_merged.json,sha256=Xem7jS8n96QEyHBmnPAvWHM4AyTLyma7Nq9sCVHuXJA,175239
|
|
42
43
|
bedrock_agentcore_starter_toolkit/services/import_agent/scripts/__init__.py,sha256=UmhcnsfIo2P1Z9VAJ6Ua2mSkxs4BOeTxgFMcgQXQWCQ,79
|
|
43
|
-
bedrock_agentcore_starter_toolkit/services/import_agent/scripts/base_bedrock_translate.py,sha256=
|
|
44
|
+
bedrock_agentcore_starter_toolkit/services/import_agent/scripts/base_bedrock_translate.py,sha256=0xxHXhsQP_FG-1RgbHqx9r8U6CrBY8GhDKRnWTmYEJQ,72751
|
|
44
45
|
bedrock_agentcore_starter_toolkit/services/import_agent/scripts/bedrock_to_langchain.py,sha256=xwoZcmZ27CfYohwSfLIgbFxa2ubiBFhvufgQEWZkmLk,14950
|
|
45
46
|
bedrock_agentcore_starter_toolkit/services/import_agent/scripts/bedrock_to_strands.py,sha256=eC3v2Ts7B_RUGl4bkUim2uFYYuZZYP9syYBlylJ2gXs,14642
|
|
46
47
|
bedrock_agentcore_starter_toolkit/utils/endpoints.py,sha256=1gIDRd1oO1fymYpiedVit7m6zl5k6N8Ns9N-2ix7ZaE,1153
|
|
@@ -48,16 +49,16 @@ bedrock_agentcore_starter_toolkit/utils/logging_config.py,sha256=NtZDyndNKCAbz7j
|
|
|
48
49
|
bedrock_agentcore_starter_toolkit/utils/runtime/config.py,sha256=qRQid59rD3zJ0d0hcBY4-8R52PNIWEIUt9iR3biuz_c,4495
|
|
49
50
|
bedrock_agentcore_starter_toolkit/utils/runtime/container.py,sha256=wGJLxwT2mhTGPxSoFb6x9lW7Tyz37YghIvSs4jBnO8A,15679
|
|
50
51
|
bedrock_agentcore_starter_toolkit/utils/runtime/entrypoint.py,sha256=d-XjEwvQOdpRHvBGLdIBCs1fgUwNwB0EL_WkcdQXwXQ,5893
|
|
51
|
-
bedrock_agentcore_starter_toolkit/utils/runtime/logs.py,sha256
|
|
52
|
+
bedrock_agentcore_starter_toolkit/utils/runtime/logs.py,sha256=ZZ9PD4QO0BSms5KphhUb3-6-IPTkmwkY-Zn2AWM9Aew,1601
|
|
52
53
|
bedrock_agentcore_starter_toolkit/utils/runtime/policy_template.py,sha256=CgER7YXPh0BpR6JcTcumDL_k8bhmfLSEok1sf09-31I,2054
|
|
53
54
|
bedrock_agentcore_starter_toolkit/utils/runtime/schema.py,sha256=gZ0zPvry-ZkXwqUEAy6Izz1RJvmZaXA6a2twnSdk1AY,6418
|
|
54
|
-
bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2,sha256=
|
|
55
|
+
bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2,sha256=X_tNR3Qw7G9rz1rtM6WP2RuqZhehzplgobT8Vx3TDok,1245
|
|
55
56
|
bedrock_agentcore_starter_toolkit/utils/runtime/templates/dockerignore.template,sha256=b_70sBi0MwkTuA9TqxPqFcWK7TcmpaXBJ6M2Ipox65Q,691
|
|
56
57
|
bedrock_agentcore_starter_toolkit/utils/runtime/templates/execution_role_policy.json.j2,sha256=-0AXT46IYVQr4fwueXTQ0FVXcCshZFNx7-2mViR34Co,4941
|
|
57
58
|
bedrock_agentcore_starter_toolkit/utils/runtime/templates/execution_role_trust_policy.json.j2,sha256=PPJF6Ofq70W5DUE0NlbmnZjw5RkgepPgkskxEgEG28o,473
|
|
58
|
-
bedrock_agentcore_starter_toolkit-0.1.
|
|
59
|
-
bedrock_agentcore_starter_toolkit-0.1.
|
|
60
|
-
bedrock_agentcore_starter_toolkit-0.1.
|
|
61
|
-
bedrock_agentcore_starter_toolkit-0.1.
|
|
62
|
-
bedrock_agentcore_starter_toolkit-0.1.
|
|
63
|
-
bedrock_agentcore_starter_toolkit-0.1.
|
|
59
|
+
bedrock_agentcore_starter_toolkit-0.1.11.dist-info/METADATA,sha256=mD8ZbSvNP-ysXpbQyPDOBTJyUa9qwNY66CH9_afM8Nc,9744
|
|
60
|
+
bedrock_agentcore_starter_toolkit-0.1.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
61
|
+
bedrock_agentcore_starter_toolkit-0.1.11.dist-info/entry_points.txt,sha256=Tf94DkUf2Tp8P7p8MEXLxre7A7Pp_XNukteiz0wHnk8,77
|
|
62
|
+
bedrock_agentcore_starter_toolkit-0.1.11.dist-info/licenses/LICENSE.txt,sha256=nNPOMinitYdtfbhdQgsPgz1UowBznU6QVN3Xs0pSTKU,10768
|
|
63
|
+
bedrock_agentcore_starter_toolkit-0.1.11.dist-info/licenses/NOTICE.txt,sha256=rkBsg8DbKqfIoQbveqX9foR4uJPUVAokbkr02pRPilE,8674
|
|
64
|
+
bedrock_agentcore_starter_toolkit-0.1.11.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|