bedrock-agentcore-starter-toolkit 0.1.0__py3-none-any.whl → 0.1.1__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 (27) hide show
  1. bedrock_agentcore_starter_toolkit/cli/cli.py +3 -10
  2. bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +52 -4
  3. bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py +20 -11
  4. bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py +53 -10
  5. bedrock_agentcore_starter_toolkit/operations/gateway/README.md +6 -6
  6. bedrock_agentcore_starter_toolkit/operations/gateway/create_role.py +11 -10
  7. bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +21 -7
  8. bedrock_agentcore_starter_toolkit/operations/runtime/create_role.py +404 -0
  9. bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +329 -53
  10. bedrock_agentcore_starter_toolkit/operations/runtime/models.py +4 -1
  11. bedrock_agentcore_starter_toolkit/services/codebuild.py +332 -0
  12. bedrock_agentcore_starter_toolkit/services/ecr.py +29 -0
  13. bedrock_agentcore_starter_toolkit/services/runtime.py +91 -1
  14. bedrock_agentcore_starter_toolkit/utils/logging_config.py +72 -0
  15. bedrock_agentcore_starter_toolkit/utils/runtime/entrypoint.py +3 -3
  16. bedrock_agentcore_starter_toolkit/utils/runtime/policy_template.py +74 -0
  17. bedrock_agentcore_starter_toolkit/utils/runtime/schema.py +12 -2
  18. bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2 +10 -25
  19. bedrock_agentcore_starter_toolkit/utils/runtime/templates/dockerignore.template +0 -1
  20. bedrock_agentcore_starter_toolkit/utils/runtime/templates/execution_role_policy.json.j2 +98 -0
  21. bedrock_agentcore_starter_toolkit/utils/runtime/templates/execution_role_trust_policy.json.j2 +21 -0
  22. {bedrock_agentcore_starter_toolkit-0.1.0.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/METADATA +7 -7
  23. {bedrock_agentcore_starter_toolkit-0.1.0.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/RECORD +27 -21
  24. {bedrock_agentcore_starter_toolkit-0.1.0.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/WHEEL +0 -0
  25. {bedrock_agentcore_starter_toolkit-0.1.0.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/entry_points.txt +0 -0
  26. {bedrock_agentcore_starter_toolkit-0.1.0.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/licenses/LICENSE.txt +0 -0
  27. {bedrock_agentcore_starter_toolkit-0.1.0.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/licenses/NOTICE.txt +0 -0
@@ -1,24 +1,242 @@
1
1
  """Launch operation - deploys Bedrock AgentCore locally or to cloud."""
2
2
 
3
+ import json
3
4
  import logging
5
+ import time
6
+ import urllib.parse
4
7
  from pathlib import Path
5
8
  from typing import Optional
6
9
 
7
- from ...services.ecr import create_ecr_repository, deploy_to_ecr
10
+ import boto3
11
+ from botocore.exceptions import ClientError
12
+
13
+ from ...services.codebuild import CodeBuildService
14
+ from ...services.ecr import deploy_to_ecr, get_or_create_ecr_repository
8
15
  from ...services.runtime import BedrockAgentCoreClient
9
16
  from ...utils.runtime.config import load_config, save_config
10
17
  from ...utils.runtime.container import ContainerRuntime
18
+ from .create_role import get_or_create_runtime_execution_role
11
19
  from .models import LaunchResult
12
20
 
13
21
  log = logging.getLogger(__name__)
14
22
 
15
23
 
24
+ def _ensure_ecr_repository(agent_config, project_config, config_path, agent_name, region):
25
+ """Ensure ECR repository exists (idempotent)."""
26
+ ecr_uri = agent_config.aws.ecr_repository
27
+
28
+ # Step 1: Check if we already have a repository in config
29
+ if ecr_uri:
30
+ log.info("Using ECR repository from config: %s", ecr_uri)
31
+ return ecr_uri
32
+
33
+ # Step 2: Create repository if needed (idempotent)
34
+ if agent_config.aws.ecr_auto_create:
35
+ log.info("Getting or creating ECR repository for agent: %s", agent_name)
36
+
37
+ ecr_uri = get_or_create_ecr_repository(agent_name, region)
38
+
39
+ # Update the config
40
+ agent_config.aws.ecr_repository = ecr_uri
41
+ agent_config.aws.ecr_auto_create = False
42
+
43
+ # Update the project config and save
44
+ project_config.agents[agent_config.name] = agent_config
45
+ save_config(project_config, config_path)
46
+
47
+ log.info("✅ ECR repository available: %s", ecr_uri)
48
+ return ecr_uri
49
+
50
+ # Step 3: No repository and auto-create disabled
51
+ raise ValueError("ECR repository not configured and auto-create not enabled")
52
+
53
+
54
+ def _validate_execution_role(role_arn: str, session: boto3.Session) -> bool:
55
+ """Validate that execution role exists and has correct trust policy for Bedrock AgentCore."""
56
+ iam = session.client("iam")
57
+ role_name = role_arn.split("/")[-1]
58
+
59
+ try:
60
+ response = iam.get_role(RoleName=role_name)
61
+ trust_policy = response["Role"]["AssumeRolePolicyDocument"]
62
+
63
+ # Parse trust policy (it might be URL-encoded)
64
+ if isinstance(trust_policy, str):
65
+ trust_policy = json.loads(urllib.parse.unquote(trust_policy))
66
+
67
+ # Check if bedrock-agentcore service can assume this role
68
+ for statement in trust_policy.get("Statement", []):
69
+ if statement.get("Effect") == "Allow":
70
+ principals = statement.get("Principal", {})
71
+
72
+ if isinstance(principals, dict):
73
+ services = principals.get("Service", [])
74
+ if isinstance(services, str):
75
+ services = [services]
76
+
77
+ if "bedrock-agentcore.amazonaws.com" in services:
78
+ return True
79
+
80
+ return False
81
+
82
+ except ClientError as e:
83
+ if e.response["Error"]["Code"] == "NoSuchEntity":
84
+ return False
85
+ raise
86
+
87
+
88
+ def _ensure_execution_role(agent_config, project_config, config_path, agent_name, region, account_id):
89
+ """Ensure execution role exists without waiting.
90
+
91
+ This function handles:
92
+ 1. Reusing existing role from config if available
93
+ 2. Creating role if needed (auto_create_execution_role=True) - now idempotent
94
+ 3. Basic validation that existing roles have correct trust policy
95
+ 4. Returning role ARN (readiness will be checked during actual deployment)
96
+ """
97
+ execution_role_arn = agent_config.aws.execution_role
98
+ session = boto3.Session(region_name=region)
99
+
100
+ # Step 1: Check if we already have a role in config
101
+ if execution_role_arn:
102
+ log.info("Using execution role from config: %s", execution_role_arn)
103
+
104
+ # Step 2: Basic validation for existing roles
105
+ if not _validate_execution_role(execution_role_arn, session):
106
+ raise ValueError(
107
+ f"Execution role {execution_role_arn} has invalid trust policy. "
108
+ "Ensure it allows bedrock-agentcore.amazonaws.com service to assume the role."
109
+ )
110
+
111
+ log.info("✅ Execution role validation passed: %s", execution_role_arn)
112
+ return execution_role_arn
113
+
114
+ # Step 3: Create role if needed (idempotent)
115
+ if agent_config.aws.execution_role_auto_create:
116
+ execution_role_arn = get_or_create_runtime_execution_role(
117
+ session=session,
118
+ logger=log,
119
+ region=region,
120
+ account_id=account_id,
121
+ agent_name=agent_name,
122
+ )
123
+
124
+ # Update the config
125
+ agent_config.aws.execution_role = execution_role_arn
126
+ agent_config.aws.execution_role_auto_create = False
127
+
128
+ # Update the project config and save
129
+ project_config.agents[agent_config.name] = agent_config
130
+ save_config(project_config, config_path)
131
+
132
+ log.info("✅ Execution role available: %s", execution_role_arn)
133
+ return execution_role_arn
134
+
135
+ # Step 4: No role and auto-create disabled
136
+ raise ValueError("Execution role not configured and auto-create not enabled")
137
+
138
+
139
+ def _deploy_to_bedrock_agentcore(
140
+ agent_config, project_config, config_path, agent_name, ecr_uri, region, env_vars=None, auto_update_on_conflict=False
141
+ ):
142
+ """Deploy agent to Bedrock AgentCore with retry logic for role validation."""
143
+ log.info("Deploying to Bedrock AgentCore...")
144
+
145
+ bedrock_agentcore_client = BedrockAgentCoreClient(region)
146
+
147
+ # Transform network configuration to AWS API format
148
+ network_config = agent_config.aws.network_configuration.to_aws_dict()
149
+ protocol_config = agent_config.aws.protocol_configuration.to_aws_dict()
150
+
151
+ # Execution role should be available by now (either provided or auto-created)
152
+ if not agent_config.aws.execution_role:
153
+ raise ValueError(
154
+ "Execution role not available. This should have been handled by _ensure_execution_role. "
155
+ "Please check configuration or enable auto-creation."
156
+ )
157
+
158
+ # Retry logic for role validation eventual consistency
159
+ max_retries = 3
160
+ base_delay = 5 # Start with 2 seconds
161
+ max_delay = 15 # Max 32 seconds between retries
162
+
163
+ for attempt in range(max_retries + 1):
164
+ try:
165
+ agent_info = bedrock_agentcore_client.create_or_update_agent(
166
+ agent_id=agent_config.bedrock_agentcore.agent_id,
167
+ agent_name=agent_name,
168
+ image_uri=f"{ecr_uri}:latest",
169
+ execution_role_arn=agent_config.aws.execution_role,
170
+ network_config=network_config,
171
+ authorizer_config=agent_config.get_authorizer_configuration(),
172
+ protocol_config=protocol_config,
173
+ env_vars=env_vars,
174
+ auto_update_on_conflict=auto_update_on_conflict,
175
+ )
176
+ break # Success! Exit retry loop
177
+
178
+ except ClientError as e:
179
+ error_code = e.response.get("Error", {}).get("Code", "")
180
+ error_message = e.response.get("Error", {}).get("Message", "")
181
+
182
+ # Check if this is a role validation error
183
+ is_role_validation_error = (
184
+ error_code == "ValidationException"
185
+ and "Role validation failed" in error_message
186
+ and agent_config.aws.execution_role in error_message
187
+ )
188
+
189
+ if not is_role_validation_error or attempt == max_retries:
190
+ # Not a role validation error, or we've exhausted retries
191
+ if is_role_validation_error:
192
+ log.error(
193
+ "Role validation failed after %d attempts. The execution role may not be ready. Role: %s",
194
+ max_retries + 1,
195
+ agent_config.aws.execution_role,
196
+ )
197
+ raise e
198
+
199
+ # Calculate delay with exponential backoff
200
+ delay = min(base_delay * (2**attempt), max_delay)
201
+ log.info(
202
+ "⏳ Role validation failed (attempt %d/%d), retrying in %ds... Role: %s",
203
+ attempt + 1,
204
+ max_retries + 1,
205
+ delay,
206
+ agent_config.aws.execution_role,
207
+ )
208
+ time.sleep(delay)
209
+
210
+ # Save deployment info
211
+ agent_id = agent_info["id"]
212
+ agent_arn = agent_info["arn"]
213
+
214
+ # Update the config
215
+ agent_config.bedrock_agentcore.agent_id = agent_id
216
+ agent_config.bedrock_agentcore.agent_arn = agent_arn
217
+
218
+ # Update the project config and save
219
+ project_config.agents[agent_config.name] = agent_config
220
+ save_config(project_config, config_path)
221
+
222
+ log.info("✅ Agent created/updated: %s", agent_arn)
223
+
224
+ # Wait for agent to be ready
225
+ log.info("Polling for endpoint to be ready...")
226
+ result = bedrock_agentcore_client.wait_for_agent_endpoint_ready(agent_id)
227
+ log.info("Agent endpoint: %s", result)
228
+
229
+ return agent_id, agent_arn
230
+
231
+
16
232
  def launch_bedrock_agentcore(
17
233
  config_path: Path,
18
234
  agent_name: Optional[str] = None,
19
235
  local: bool = False,
20
236
  push_ecr_only: bool = False,
237
+ use_codebuild: bool = False,
21
238
  env_vars: Optional[dict] = None,
239
+ auto_update_on_conflict: bool = False,
22
240
  ) -> LaunchResult:
23
241
  """Launch Bedrock AgentCore locally or to cloud.
24
242
 
@@ -27,7 +245,9 @@ def launch_bedrock_agentcore(
27
245
  agent_name: Name of agent to launch (for project configurations)
28
246
  local: Whether to run locally
29
247
  push_ecr_only: Whether to only build and push to ECR without deploying
248
+ use_codebuild: Whether to use CodeBuild for ARM64 builds
30
249
  env_vars: Environment variables to pass to local container (dict of key-value pairs)
250
+ auto_update_on_conflict: Whether to automatically update when agent already exists (default: False)
31
251
 
32
252
  Returns:
33
253
  LaunchResult model with launch details
@@ -36,6 +256,16 @@ def launch_bedrock_agentcore(
36
256
  project_config = load_config(config_path)
37
257
  agent_config = project_config.get_agent_config(agent_name)
38
258
 
259
+ # Handle CodeBuild deployment
260
+ if use_codebuild:
261
+ return _launch_with_codebuild(
262
+ config_path=config_path,
263
+ agent_name=agent_config.name,
264
+ agent_config=agent_config,
265
+ project_config=project_config,
266
+ auto_update_on_conflict=auto_update_on_conflict,
267
+ )
268
+
39
269
  # Log which agent is being launched
40
270
  mode = "locally" if local else "to ECR only" if push_ecr_only else "to cloud"
41
271
  log.info("Launching Bedrock AgentCore agent '%s' %s", agent_config.name, mode)
@@ -72,29 +302,20 @@ def launch_bedrock_agentcore(
72
302
  env_vars=env_vars,
73
303
  )
74
304
 
75
- # Step 2: Push to ECR
76
- log.info("Uploading to ECR...")
77
-
78
305
  region = agent_config.aws.region
79
306
  if not region:
80
307
  raise ValueError("Region not found in configuration")
81
308
 
82
- # Handle ECR repository
83
- ecr_uri = agent_config.aws.ecr_repository
84
- if not ecr_uri and agent_config.aws.ecr_auto_create:
85
- repo_name = f"bedrock_agentcore-{bedrock_agentcore_name}"
86
- ecr_uri = create_ecr_repository(repo_name, region)
309
+ account_id = agent_config.aws.account
87
310
 
88
- # Update the config
89
- agent_config.aws.ecr_repository = ecr_uri
90
- agent_config.aws.ecr_auto_create = False
311
+ # Step 2: Ensure execution role exists (moved before ECR push)
312
+ _ensure_execution_role(agent_config, project_config, config_path, bedrock_agentcore_name, region, account_id)
91
313
 
92
- # Update the project config and save
93
- project_config.agents[agent_config.name] = agent_config
94
- save_config(project_config, config_path)
314
+ # Step 3: Push to ECR
315
+ log.info("Uploading to ECR...")
95
316
 
96
- if not ecr_uri:
97
- raise ValueError("ECR repository not configured and auto-create not enabled")
317
+ # Handle ECR repository
318
+ ecr_uri = _ensure_ecr_repository(agent_config, project_config, config_path, bedrock_agentcore_name, region)
98
319
 
99
320
  # Deploy to ECR
100
321
  repo_name = ecr_uri.split("/")[-1]
@@ -111,53 +332,108 @@ def launch_bedrock_agentcore(
111
332
  build_output=output,
112
333
  )
113
334
 
114
- # Step 3: Deploy agent
115
- log.info("Creating/updating agent...")
335
+ # Step 4: Deploy agent (with retry logic for role readiness)
336
+ agent_id, agent_arn = _deploy_to_bedrock_agentcore(
337
+ agent_config,
338
+ project_config,
339
+ config_path,
340
+ bedrock_agentcore_name,
341
+ ecr_uri,
342
+ region,
343
+ env_vars,
344
+ auto_update_on_conflict,
345
+ )
116
346
 
117
- bedrock_agentcore_client = BedrockAgentCoreClient(region)
347
+ return LaunchResult(
348
+ mode="cloud",
349
+ tag=tag,
350
+ agent_arn=agent_arn,
351
+ agent_id=agent_id,
352
+ ecr_uri=ecr_uri,
353
+ build_output=output,
354
+ )
118
355
 
119
- # Transform network configuration to AWS API format
120
- network_config = agent_config.aws.network_configuration.to_aws_dict()
121
- protocol_config = agent_config.aws.protocol_configuration.to_aws_dict()
122
356
 
123
- if not agent_config.aws.execution_role:
124
- raise ValueError("Execution role not configured")
125
-
126
- agent_info = bedrock_agentcore_client.create_or_update_agent(
127
- agent_id=agent_config.bedrock_agentcore.agent_id,
128
- agent_name=bedrock_agentcore_name,
129
- image_uri=f"{ecr_uri}:latest",
130
- execution_role_arn=agent_config.aws.execution_role,
131
- network_config=network_config,
132
- authorizer_config=agent_config.get_authorizer_configuration(),
133
- protocol_config=protocol_config,
134
- env_vars=env_vars,
357
+ def _launch_with_codebuild(
358
+ config_path: Path,
359
+ agent_name: str,
360
+ agent_config,
361
+ project_config,
362
+ auto_update_on_conflict: bool = False,
363
+ ) -> LaunchResult:
364
+ """Launch using CodeBuild for ARM64 builds."""
365
+ log.info(
366
+ "Starting CodeBuild ARM64 deployment for agent '%s' to account %s (%s)",
367
+ agent_name,
368
+ agent_config.aws.account,
369
+ agent_config.aws.region,
135
370
  )
136
371
 
137
- # Save deployment info
138
- agent_id = agent_info["id"]
139
- agent_arn = agent_info["arn"]
372
+ # Validate configuration
373
+ errors = agent_config.validate(for_local=False)
374
+ if errors:
375
+ raise ValueError(f"Invalid configuration: {', '.join(errors)}")
140
376
 
141
- # Update the config
142
- agent_config.bedrock_agentcore.agent_id = agent_id
143
- agent_config.bedrock_agentcore.agent_arn = agent_arn
377
+ region = agent_config.aws.region
378
+ if not region:
379
+ raise ValueError("Region not found in configuration")
144
380
 
145
- # Update the project config and save
146
- project_config.agents[agent_config.name] = agent_config
147
- save_config(project_config, config_path)
381
+ session = boto3.Session(region_name=region)
382
+ account_id = agent_config.aws.account # Use existing account from config
148
383
 
149
- log.info("Agent created/updated: %s", agent_arn)
384
+ # Step 1: Setup AWS resources
385
+ log.info("Setting up AWS resources (ECR repository, execution roles)...")
386
+ ecr_uri = _ensure_ecr_repository(agent_config, project_config, config_path, agent_name, region)
387
+ ecr_repository_arn = f"arn:aws:ecr:{region}:{account_id}:repository/{ecr_uri.split('/')[-1]}"
388
+ _ensure_execution_role(agent_config, project_config, config_path, agent_name, region, account_id)
150
389
 
151
- # Step 4: Wait for agent to be ready
152
- log.info("Polling for endpoint to be ready...")
153
- result = bedrock_agentcore_client.wait_for_agent_endpoint_ready(agent_id)
154
- log.info("Agent endpoint: %s", result)
390
+ # Step 2: Prepare CodeBuild
391
+ log.info("Preparing CodeBuild project and uploading source...")
392
+ codebuild_service = CodeBuildService(session)
393
+
394
+ codebuild_execution_role = codebuild_service.create_codebuild_execution_role(
395
+ account_id=account_id, ecr_repository_arn=ecr_repository_arn, agent_name=agent_name
396
+ )
397
+
398
+ source_location = codebuild_service.upload_source(agent_name=agent_name)
399
+
400
+ project_name = codebuild_service.create_or_update_project(
401
+ agent_name=agent_name,
402
+ ecr_repository_uri=ecr_uri,
403
+ execution_role=codebuild_execution_role,
404
+ source_location=source_location,
405
+ )
406
+
407
+ # Step 3: Execute CodeBuild
408
+ log.info("Starting CodeBuild build (this may take several minutes)...")
409
+ build_id = codebuild_service.start_build(project_name, source_location)
410
+ codebuild_service.wait_for_completion(build_id)
411
+ log.info("CodeBuild completed successfully")
412
+
413
+ # Update CodeBuild config
414
+ agent_config.codebuild.project_name = project_name
415
+ agent_config.codebuild.execution_role = codebuild_execution_role
416
+ agent_config.codebuild.source_bucket = codebuild_service.source_bucket
417
+
418
+ # Deploy to Bedrock AgentCore
419
+ agent_id, agent_arn = _deploy_to_bedrock_agentcore(
420
+ agent_config,
421
+ project_config,
422
+ config_path,
423
+ agent_name,
424
+ ecr_uri,
425
+ region,
426
+ env_vars=None,
427
+ auto_update_on_conflict=auto_update_on_conflict,
428
+ )
429
+
430
+ log.info("Deployment completed successfully - Agent: %s", agent_arn)
155
431
 
156
432
  return LaunchResult(
157
- mode="cloud",
158
- tag=tag,
433
+ mode="codebuild",
434
+ tag=f"bedrock_agentcore-{agent_name}:latest",
435
+ codebuild_id=build_id,
436
+ ecr_uri=ecr_uri,
159
437
  agent_arn=agent_arn,
160
438
  agent_id=agent_id,
161
- ecr_uri=ecr_uri,
162
- build_output=output,
163
439
  )
@@ -27,7 +27,7 @@ class ConfigureResult(BaseModel):
27
27
  class LaunchResult(BaseModel):
28
28
  """Result of launch operation."""
29
29
 
30
- mode: str = Field(..., description="Launch mode: local, push-ecr, or cloud")
30
+ mode: str = Field(..., description="Launch mode: local, push-ecr, cloud, or codebuild")
31
31
  tag: str = Field(..., description="Docker image tag")
32
32
  env_vars: Optional[Dict[str, str]] = Field(default=None, description="Environment variables for local deployment")
33
33
 
@@ -40,6 +40,9 @@ class LaunchResult(BaseModel):
40
40
  agent_id: Optional[str] = Field(default=None, description="BedrockAgentCore agent ID")
41
41
  agent_arn: Optional[str] = Field(default=None, description="BedrockAgentCore agent ARN")
42
42
 
43
+ # CodeBuild mode fields
44
+ codebuild_id: Optional[str] = Field(default=None, description="CodeBuild build ID for ARM64 builds")
45
+
43
46
  # Build output (optional)
44
47
  build_output: Optional[List[str]] = Field(default=None, description="Docker build output")
45
48