bedrock-agentcore-starter-toolkit 0.0.1__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 (50) hide show
  1. bedrock_agentcore_starter_toolkit/__init__.py +5 -0
  2. bedrock_agentcore_starter_toolkit/cli/cli.py +32 -0
  3. bedrock_agentcore_starter_toolkit/cli/common.py +44 -0
  4. bedrock_agentcore_starter_toolkit/cli/gateway/__init__.py +1 -0
  5. bedrock_agentcore_starter_toolkit/cli/gateway/commands.py +88 -0
  6. bedrock_agentcore_starter_toolkit/cli/runtime/__init__.py +1 -0
  7. bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +651 -0
  8. bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py +133 -0
  9. bedrock_agentcore_starter_toolkit/notebook/__init__.py +5 -0
  10. bedrock_agentcore_starter_toolkit/notebook/runtime/__init__.py +1 -0
  11. bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py +239 -0
  12. bedrock_agentcore_starter_toolkit/operations/__init__.py +1 -0
  13. bedrock_agentcore_starter_toolkit/operations/gateway/README.md +277 -0
  14. bedrock_agentcore_starter_toolkit/operations/gateway/__init__.py +6 -0
  15. bedrock_agentcore_starter_toolkit/operations/gateway/client.py +456 -0
  16. bedrock_agentcore_starter_toolkit/operations/gateway/constants.py +152 -0
  17. bedrock_agentcore_starter_toolkit/operations/gateway/create_lambda.py +85 -0
  18. bedrock_agentcore_starter_toolkit/operations/gateway/create_role.py +90 -0
  19. bedrock_agentcore_starter_toolkit/operations/gateway/exceptions.py +13 -0
  20. bedrock_agentcore_starter_toolkit/operations/runtime/__init__.py +26 -0
  21. bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +241 -0
  22. bedrock_agentcore_starter_toolkit/operations/runtime/create_role.py +404 -0
  23. bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py +129 -0
  24. bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +439 -0
  25. bedrock_agentcore_starter_toolkit/operations/runtime/models.py +79 -0
  26. bedrock_agentcore_starter_toolkit/operations/runtime/status.py +66 -0
  27. bedrock_agentcore_starter_toolkit/services/codebuild.py +332 -0
  28. bedrock_agentcore_starter_toolkit/services/ecr.py +84 -0
  29. bedrock_agentcore_starter_toolkit/services/runtime.py +473 -0
  30. bedrock_agentcore_starter_toolkit/utils/endpoints.py +32 -0
  31. bedrock_agentcore_starter_toolkit/utils/logging_config.py +72 -0
  32. bedrock_agentcore_starter_toolkit/utils/runtime/config.py +129 -0
  33. bedrock_agentcore_starter_toolkit/utils/runtime/container.py +310 -0
  34. bedrock_agentcore_starter_toolkit/utils/runtime/entrypoint.py +197 -0
  35. bedrock_agentcore_starter_toolkit/utils/runtime/logs.py +33 -0
  36. bedrock_agentcore_starter_toolkit/utils/runtime/policy_template.py +74 -0
  37. bedrock_agentcore_starter_toolkit/utils/runtime/schema.py +151 -0
  38. bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2 +44 -0
  39. bedrock_agentcore_starter_toolkit/utils/runtime/templates/dockerignore.template +68 -0
  40. bedrock_agentcore_starter_toolkit/utils/runtime/templates/execution_role_policy.json.j2 +98 -0
  41. bedrock_agentcore_starter_toolkit/utils/runtime/templates/execution_role_trust_policy.json.j2 +21 -0
  42. bedrock_agentcore_starter_toolkit-0.1.1.dist-info/METADATA +137 -0
  43. bedrock_agentcore_starter_toolkit-0.1.1.dist-info/RECORD +47 -0
  44. bedrock_agentcore_starter_toolkit-0.1.1.dist-info/entry_points.txt +2 -0
  45. bedrock_agentcore_starter_toolkit-0.1.1.dist-info/licenses/NOTICE.txt +190 -0
  46. bedrock_agentcore_starter_toolkit/init.py +0 -3
  47. bedrock_agentcore_starter_toolkit-0.0.1.dist-info/METADATA +0 -26
  48. bedrock_agentcore_starter_toolkit-0.0.1.dist-info/RECORD +0 -5
  49. {bedrock_agentcore_starter_toolkit-0.0.1.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/WHEEL +0 -0
  50. /bedrock_agentcore_starter_toolkit-0.0.1.dist-info/licenses/LICENSE → /bedrock_agentcore_starter_toolkit-0.1.1.dist-info/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,473 @@
1
+ """BedrockAgentCore service client for agent management."""
2
+
3
+ import json
4
+ import logging
5
+ import time
6
+ import urllib.parse
7
+ import uuid
8
+ from typing import Any, Dict, Optional
9
+
10
+ import boto3
11
+ import requests
12
+ from botocore.exceptions import ClientError
13
+
14
+ from ..utils.endpoints import get_control_plane_endpoint, get_data_plane_endpoint
15
+
16
+
17
+ def generate_session_id() -> str:
18
+ """Generate session ID."""
19
+ return str(uuid.uuid4())
20
+
21
+
22
+ def _handle_http_response(response) -> dict:
23
+ response.raise_for_status()
24
+ if "text/event-stream" in response.headers.get("content-type", ""):
25
+ return _handle_streaming_response(response)
26
+ else:
27
+ # Check if response has content
28
+ if not response.content:
29
+ raise ValueError("Empty response from agent endpoint")
30
+
31
+ return {"response": response.text}
32
+
33
+
34
+ def _handle_aws_response(response) -> dict:
35
+ if "text/event-stream" in response.get("contentType", ""):
36
+ return _handle_streaming_response(response["response"])
37
+ else:
38
+ try:
39
+ events = []
40
+ for event in response.get("response", []):
41
+ events.append(event)
42
+ except Exception as e:
43
+ events = [f"Error reading EventStream: {e}"]
44
+
45
+ response["response"] = events
46
+ return response
47
+
48
+
49
+ def _handle_streaming_response(response) -> Dict[str, Any]:
50
+ logger = logging.getLogger("bedrock_agentcore.stream")
51
+ logger.setLevel(logging.INFO)
52
+
53
+ content = []
54
+ for line in response.iter_lines(chunk_size=1):
55
+ if line:
56
+ line = line.decode("utf-8")
57
+ if line.startswith("data: "):
58
+ line = line[6:]
59
+ logger.info(line)
60
+ content.append(line)
61
+
62
+ return {"response": "\n".join(content)}
63
+
64
+
65
+ class BedrockAgentCoreClient:
66
+ """Bedrock AgentCore client for agent management."""
67
+
68
+ def __init__(self, region: str):
69
+ """Initialize Bedrock AgentCore client.
70
+
71
+ Args:
72
+ region: AWS region for the client
73
+ """
74
+ self.region = region
75
+ self.logger = logging.getLogger(f"bedrock_agentcore.runtime.{region}")
76
+
77
+ # Get endpoint URLs and log them
78
+ control_plane_url = get_control_plane_endpoint(region)
79
+ data_plane_url = get_data_plane_endpoint(region)
80
+
81
+ self.logger.debug("Initializing Bedrock AgentCore client for region: %s", region)
82
+ self.logger.debug("Control plane: %s", control_plane_url)
83
+ self.logger.debug("Data plane: %s", data_plane_url)
84
+
85
+ self.client = boto3.client("bedrock-agentcore-control", region_name=region, endpoint_url=control_plane_url)
86
+ self.dataplane_client = boto3.client("bedrock-agentcore", region_name=region, endpoint_url=data_plane_url)
87
+
88
+ def create_agent(
89
+ self,
90
+ agent_name: str,
91
+ image_uri: str,
92
+ execution_role_arn: str,
93
+ network_config: Optional[Dict] = None,
94
+ authorizer_config: Optional[Dict] = None,
95
+ protocol_config: Optional[Dict] = None,
96
+ env_vars: Optional[Dict] = None,
97
+ auto_update_on_conflict: bool = False,
98
+ ) -> Dict[str, str]:
99
+ """Create new agent."""
100
+ self.logger.info("Creating agent '%s' with image URI: %s", agent_name, image_uri)
101
+ try:
102
+ # Build parameters dict, only including optional configs when present
103
+ params = {
104
+ "agentRuntimeName": agent_name,
105
+ "agentRuntimeArtifact": {"containerConfiguration": {"containerUri": image_uri}},
106
+ "roleArn": execution_role_arn,
107
+ }
108
+
109
+ if network_config is not None:
110
+ params["networkConfiguration"] = network_config
111
+
112
+ if authorizer_config is not None:
113
+ params["authorizerConfiguration"] = authorizer_config
114
+
115
+ if protocol_config is not None:
116
+ params["protocolConfiguration"] = protocol_config
117
+
118
+ if env_vars is not None:
119
+ params["environmentVariables"] = env_vars
120
+
121
+ resp = self.client.create_agent_runtime(**params)
122
+ agent_id = resp["agentRuntimeId"]
123
+ agent_arn = resp["agentRuntimeArn"]
124
+ self.logger.info("Successfully created agent '%s' with ID: %s, ARN: %s", agent_name, agent_id, agent_arn)
125
+ return {"id": agent_id, "arn": agent_arn}
126
+ except ClientError as e:
127
+ error_code = e.response.get("Error", {}).get("Code")
128
+ if error_code == "ConflictException":
129
+ if not auto_update_on_conflict:
130
+ self.logger.error("Agent '%s' already exists and auto_update_on_conflict is disabled", agent_name)
131
+ raise
132
+
133
+ self.logger.info("Agent '%s' already exists, searching for existing agent...", agent_name)
134
+
135
+ # Find existing agent by name
136
+ existing_agent = self.find_agent_by_name(agent_name)
137
+
138
+ if not existing_agent:
139
+ raise RuntimeError(
140
+ f"ConflictException occurred but couldn't find existing agent '{agent_name}'. "
141
+ f"This might be a permissions issue or the agent name might be different."
142
+ ) from e
143
+
144
+ # Extract existing agent details
145
+ existing_agent_id = existing_agent["agentRuntimeId"]
146
+ existing_agent_arn = existing_agent["agentRuntimeArn"]
147
+
148
+ self.logger.info("Found existing agent ID: %s, updating instead...", existing_agent_id)
149
+
150
+ # Update the existing agent
151
+ self.update_agent(
152
+ existing_agent_id,
153
+ image_uri,
154
+ execution_role_arn,
155
+ network_config,
156
+ authorizer_config,
157
+ protocol_config,
158
+ env_vars,
159
+ )
160
+
161
+ # Return the existing agent info (keeping the original ID and ARN)
162
+ return {"id": existing_agent_id, "arn": existing_agent_arn}
163
+ else:
164
+ # Re-raise other ClientErrors
165
+ raise
166
+ except Exception as e:
167
+ self.logger.error("Failed to create agent '%s': %s", agent_name, str(e))
168
+ raise
169
+
170
+ def update_agent(
171
+ self,
172
+ agent_id: str,
173
+ image_uri: str,
174
+ execution_role_arn: str,
175
+ network_config: Optional[Dict] = None,
176
+ authorizer_config: Optional[Dict] = None,
177
+ protocol_config: Optional[Dict] = None,
178
+ env_vars: Optional[Dict] = None,
179
+ ) -> Dict[str, str]:
180
+ """Update existing agent."""
181
+ self.logger.info("Updating agent ID '%s' with image URI: %s", agent_id, image_uri)
182
+ try:
183
+ # Build parameters dict, only including optional configs when present
184
+ params = {
185
+ "agentRuntimeId": agent_id,
186
+ "agentRuntimeArtifact": {"containerConfiguration": {"containerUri": image_uri}},
187
+ "roleArn": execution_role_arn,
188
+ }
189
+
190
+ if network_config is not None:
191
+ params["networkConfiguration"] = network_config
192
+
193
+ if authorizer_config is not None:
194
+ params["authorizerConfiguration"] = authorizer_config
195
+
196
+ if protocol_config is not None:
197
+ params["protocolConfiguration"] = protocol_config
198
+
199
+ if env_vars is not None:
200
+ params["environmentVariables"] = env_vars
201
+
202
+ resp = self.client.update_agent_runtime(**params)
203
+ agent_arn = resp["agentRuntimeArn"]
204
+ self.logger.info("Successfully updated agent ID '%s', ARN: %s", agent_id, agent_arn)
205
+ return {"id": agent_id, "arn": agent_arn}
206
+ except Exception as e:
207
+ self.logger.error("Failed to update agent ID '%s': %s", agent_id, str(e))
208
+ raise
209
+
210
+ def list_agents(self, max_results: int = 100) -> list:
211
+ """List all agent runtimes, handling pagination."""
212
+ all_agents = []
213
+ next_token = None
214
+
215
+ try:
216
+ while True:
217
+ params = {"maxResults": max_results}
218
+ if next_token:
219
+ params["nextToken"] = next_token
220
+
221
+ response = self.client.list_agent_runtimes(**params)
222
+ agents = response.get("agentRuntimes", [])
223
+ all_agents.extend(agents)
224
+
225
+ next_token = response.get("nextToken")
226
+ if not next_token:
227
+ break
228
+
229
+ return all_agents
230
+ except Exception as e:
231
+ self.logger.error("Failed to list agents: %s", str(e))
232
+ raise
233
+
234
+ def find_agent_by_name(self, agent_name: str) -> Optional[Dict]:
235
+ """Find an agent by name, reusing list_agents method."""
236
+ try:
237
+ # Get all agents using the existing method
238
+ all_agents = self.list_agents()
239
+
240
+ # Search for the specific agent by name
241
+ for agent in all_agents:
242
+ if agent.get("agentRuntimeName") == agent_name:
243
+ return agent
244
+
245
+ return None # Agent not found
246
+ except Exception as e:
247
+ self.logger.error("Failed to search for agent '%s': %s", agent_name, str(e))
248
+ raise
249
+
250
+ def create_or_update_agent(
251
+ self,
252
+ agent_id: Optional[str],
253
+ agent_name: str,
254
+ image_uri: str,
255
+ execution_role_arn: str,
256
+ network_config: Optional[Dict] = None,
257
+ authorizer_config: Optional[Dict] = None,
258
+ protocol_config: Optional[Dict] = None,
259
+ env_vars: Optional[Dict] = None,
260
+ auto_update_on_conflict: bool = False,
261
+ ) -> Dict[str, str]:
262
+ """Create or update agent."""
263
+ if agent_id:
264
+ return self.update_agent(
265
+ agent_id, image_uri, execution_role_arn, network_config, authorizer_config, protocol_config, env_vars
266
+ )
267
+ return self.create_agent(
268
+ agent_name,
269
+ image_uri,
270
+ execution_role_arn,
271
+ network_config,
272
+ authorizer_config,
273
+ protocol_config,
274
+ env_vars,
275
+ auto_update_on_conflict,
276
+ )
277
+
278
+ def wait_for_agent_endpoint_ready(self, agent_id: str, endpoint_name: str = "DEFAULT", max_wait: int = 120) -> str:
279
+ """Wait for agent endpoint to be ready.
280
+
281
+ Args:
282
+ agent_id: Agent ID to wait for
283
+ endpoint_name: Endpoint name, defaults to "DEFAULT"
284
+ max_wait: Maximum wait time in seconds
285
+
286
+ Returns:
287
+ Agent endpoint ARN when ready
288
+ """
289
+ start_time = time.time()
290
+
291
+ while time.time() - start_time < max_wait:
292
+ try:
293
+ resp = self.client.get_agent_runtime_endpoint(
294
+ agentRuntimeId=agent_id,
295
+ endpointName=endpoint_name,
296
+ )
297
+ status = resp.get("status", "UNKNOWN")
298
+
299
+ if status == "READY":
300
+ return resp["agentRuntimeEndpointArn"]
301
+ elif status in ["CREATE_FAILED", "UPDATE_FAILED"]:
302
+ raise Exception(
303
+ f"Agent endpoint {status.lower().replace('_', ' ')}: {resp.get('failureReason', 'Unknown')}"
304
+ )
305
+ elif status not in ["CREATING", "UPDATING"]:
306
+ pass
307
+ except self.client.exceptions.ResourceNotFoundException:
308
+ pass
309
+ except Exception as e:
310
+ if "ResourceNotFoundException" not in str(e):
311
+ raise
312
+ time.sleep(2)
313
+ return (
314
+ f"Endpoint is taking longer than {max_wait} seconds to be ready, "
315
+ f"please check status and try to invoke after some time"
316
+ )
317
+
318
+ def get_agent_runtime(self, agent_id: str) -> Dict:
319
+ """Get agent runtime details.
320
+
321
+ Args:
322
+ agent_id: Agent ID to get details for
323
+
324
+ Returns:
325
+ Agent runtime details
326
+ """
327
+ return self.client.get_agent_runtime(agentRuntimeId=agent_id)
328
+
329
+ def get_agent_runtime_endpoint(self, agent_id: str, endpoint_name: str = "DEFAULT") -> Dict:
330
+ """Get agent runtime endpoint details.
331
+
332
+ Args:
333
+ agent_id: Agent ID to get endpoint for
334
+ endpoint_name: Endpoint name, defaults to "DEFAULT"
335
+
336
+ Returns:
337
+ Agent endpoint details
338
+ """
339
+ return self.client.get_agent_runtime_endpoint(
340
+ agentRuntimeId=agent_id,
341
+ endpointName=endpoint_name,
342
+ )
343
+
344
+ def invoke_endpoint(
345
+ self,
346
+ agent_arn: str,
347
+ payload: str,
348
+ session_id: str,
349
+ endpoint_name: str = "DEFAULT",
350
+ user_id: Optional[str] = None,
351
+ ) -> Dict:
352
+ """Invoke agent endpoint."""
353
+ req = {
354
+ "agentRuntimeArn": agent_arn,
355
+ "qualifier": endpoint_name,
356
+ "runtimeSessionId": session_id,
357
+ "payload": payload,
358
+ }
359
+
360
+ if user_id:
361
+ req["runtimeUserId"] = user_id
362
+
363
+ response = self.dataplane_client.invoke_agent_runtime(**req)
364
+ return _handle_aws_response(response)
365
+
366
+
367
+ class HttpBedrockAgentCoreClient:
368
+ """Bedrock AgentCore client for agent management using HTTP requests with bearer token."""
369
+
370
+ def __init__(self, region: str):
371
+ """Initialize HttpBedrockAgentCoreClient.
372
+
373
+ Args:
374
+ region: AWS region for the client
375
+ """
376
+ self.region = region
377
+ self.dp_endpoint = get_data_plane_endpoint(region)
378
+ self.logger = logging.getLogger(f"bedrock_agentcore.http_runtime.{region}")
379
+
380
+ self.logger.debug("Initializing HTTP Bedrock AgentCore client for region: %s", region)
381
+ self.logger.debug("Data plane: %s", self.dp_endpoint)
382
+
383
+ def invoke_endpoint(
384
+ self,
385
+ agent_arn: str,
386
+ payload,
387
+ session_id: str,
388
+ bearer_token: Optional[str],
389
+ endpoint_name: str = "DEFAULT",
390
+ ) -> Dict:
391
+ """Invoke agent endpoint using HTTP request with bearer token.
392
+
393
+ Args:
394
+ agent_arn: Agent ARN to invoke
395
+ payload: Payload to send (dict or string)
396
+ session_id: Session ID for the request
397
+ bearer_token: Bearer token for authentication
398
+ endpoint_name: Endpoint name, defaults to "DEFAULT"
399
+
400
+ Returns:
401
+ Response from the agent endpoint
402
+ """
403
+ # Escape agent ARN for URL
404
+ escaped_arn = urllib.parse.quote(agent_arn, safe="")
405
+
406
+ # Build URL
407
+ url = f"{self.dp_endpoint}/runtimes/{escaped_arn}/invocations"
408
+ # Headers
409
+ headers = {
410
+ "Authorization": f"Bearer {bearer_token}",
411
+ "Content-Type": "application/json",
412
+ "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": session_id,
413
+ }
414
+
415
+ # Parse the payload string back to JSON object to send properly
416
+ # This ensures consistent payload structure between boto3 and HTTP clients
417
+ try:
418
+ body = json.loads(payload) if isinstance(payload, str) else payload
419
+ except json.JSONDecodeError:
420
+ # Fallback for non-JSON strings - wrap in payload object
421
+ self.logger.warning("Failed to parse payload as JSON, wrapping in payload object")
422
+ body = {"payload": payload}
423
+
424
+ try:
425
+ # Make request with timeout
426
+ response = requests.post(
427
+ url,
428
+ params={"qualifier": endpoint_name},
429
+ headers=headers,
430
+ json=body,
431
+ timeout=100,
432
+ stream=True,
433
+ )
434
+ return _handle_http_response(response)
435
+ except requests.exceptions.RequestException as e:
436
+ self.logger.error("Failed to invoke agent endpoint: %s", str(e))
437
+ raise
438
+
439
+
440
+ class LocalBedrockAgentCoreClient:
441
+ """Local Bedrock AgentCore client for invoking endpoints."""
442
+
443
+ def __init__(self, endpoint: str):
444
+ """Initialize the local client with the given endpoint."""
445
+ self.endpoint = endpoint
446
+ self.logger = logging.getLogger("bedrock_agentcore.http_local")
447
+
448
+ def invoke_endpoint(self, session_id: str, payload: str, workload_access_token: str):
449
+ """Invoke the endpoint with the given parameters."""
450
+ from bedrock_agentcore.runtime.models import ACCESS_TOKEN_HEADER, SESSION_HEADER
451
+
452
+ url = f"{self.endpoint}/invocations"
453
+
454
+ headers = {
455
+ "Content-Type": "application/json",
456
+ ACCESS_TOKEN_HEADER: workload_access_token,
457
+ SESSION_HEADER: session_id,
458
+ }
459
+
460
+ try:
461
+ body = json.loads(payload) if isinstance(payload, str) else payload
462
+ except json.JSONDecodeError:
463
+ # Fallback for non-JSON strings - wrap in payload object
464
+ self.logger.warning("Failed to parse payload as JSON, wrapping in payload object")
465
+ body = {"payload": payload}
466
+
467
+ try:
468
+ # Make request with timeout
469
+ response = requests.post(url, headers=headers, json=body, timeout=100, stream=True)
470
+ return _handle_http_response(response)
471
+ except requests.exceptions.RequestException as e:
472
+ self.logger.error("Failed to invoke agent endpoint: %s", str(e))
473
+ raise
@@ -0,0 +1,32 @@
1
+ """Endpoint utilities for BedrockAgentCore services."""
2
+
3
+ import os
4
+
5
+ # Environment-configurable constants with fallback defaults
6
+ DP_ENDPOINT_OVERRIDE = os.getenv("BEDROCK_AGENTCORE_DP_ENDPOINT")
7
+ CP_ENDPOINT_OVERRIDE = os.getenv("BEDROCK_AGENTCORE_CP_ENDPOINT")
8
+ DEFAULT_REGION = os.getenv("AWS_REGION", "us-west-2")
9
+
10
+
11
+ def get_data_plane_endpoint(region: str = DEFAULT_REGION) -> str:
12
+ """Get the data plane endpoint URL for BedrockAgentCore services.
13
+
14
+ Args:
15
+ region: AWS region to use. Defaults to DEFAULT_REGION.
16
+
17
+ Returns:
18
+ The data plane endpoint URL, either from environment override or constructed URL.
19
+ """
20
+ return DP_ENDPOINT_OVERRIDE or f"https://bedrock-agentcore.{region}.amazonaws.com"
21
+
22
+
23
+ def get_control_plane_endpoint(region: str = DEFAULT_REGION) -> str:
24
+ """Get the control plane endpoint URL for BedrockAgentCore services.
25
+
26
+ Args:
27
+ region: AWS region to use. Defaults to DEFAULT_REGION.
28
+
29
+ Returns:
30
+ The control plane endpoint URL, either from environment override or constructed URL.
31
+ """
32
+ return CP_ENDPOINT_OVERRIDE or f"https://bedrock-agentcore-control.{region}.amazonaws.com"
@@ -0,0 +1,72 @@
1
+ """Centralized logging configuration for bedrock-agentcore-starter-toolkit."""
2
+
3
+ import logging
4
+
5
+ _LOGGING_CONFIGURED = False
6
+
7
+
8
+ def setup_toolkit_logging(mode: str = "sdk") -> None:
9
+ """Setup logging for bedrock-agentcore-starter-toolkit.
10
+
11
+ Args:
12
+ mode: "cli" or "sdk" (defaults to "sdk")
13
+ """
14
+ global _LOGGING_CONFIGURED
15
+ if _LOGGING_CONFIGURED:
16
+ return # Already configured, prevent duplicates
17
+
18
+ if mode == "cli":
19
+ _setup_cli_logging()
20
+ elif mode == "sdk":
21
+ _setup_sdk_logging()
22
+ else:
23
+ raise ValueError(f"Invalid logging mode: {mode}. Must be 'cli' or 'sdk'")
24
+
25
+ _LOGGING_CONFIGURED = True
26
+
27
+
28
+ def _setup_cli_logging() -> None:
29
+ """Setup logging for CLI usage with RichHandler."""
30
+ try:
31
+ from rich.logging import RichHandler
32
+
33
+ from ..cli.common import console
34
+
35
+ FORMAT = "%(message)s"
36
+ logging.basicConfig(
37
+ level="INFO",
38
+ format=FORMAT,
39
+ handlers=[RichHandler(show_time=False, show_path=False, show_level=False, console=console)],
40
+ force=True, # Override any existing configuration
41
+ )
42
+ except ImportError:
43
+ # Fallback if rich is not available
44
+ _setup_basic_logging()
45
+
46
+
47
+ def _setup_sdk_logging() -> None:
48
+ """Setup logging for SDK usage (notebooks, scripts, imports) with StreamHandler."""
49
+ # Configure logger for ALL toolkit modules (ensures all operation logs appear)
50
+ toolkit_logger = logging.getLogger("bedrock_agentcore_starter_toolkit")
51
+
52
+ if not toolkit_logger.handlers:
53
+ handler = logging.StreamHandler()
54
+ handler.setFormatter(logging.Formatter("%(message)s"))
55
+ toolkit_logger.addHandler(handler)
56
+ toolkit_logger.setLevel(logging.INFO)
57
+
58
+
59
+ def _setup_basic_logging() -> None:
60
+ """Setup basic logging as fallback."""
61
+ logging.basicConfig(level=logging.INFO, format="%(message)s", force=True)
62
+
63
+
64
+ def is_logging_configured() -> bool:
65
+ """Check if toolkit logging has been configured."""
66
+ return _LOGGING_CONFIGURED
67
+
68
+
69
+ def reset_logging_config() -> None:
70
+ """Reset logging configuration state (for testing)."""
71
+ global _LOGGING_CONFIGURED
72
+ _LOGGING_CONFIGURED = False