bedrock-agentcore-starter-toolkit 0.0.1__py3-none-any.whl → 0.1.0__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/__init__.py +5 -0
- bedrock_agentcore_starter_toolkit/cli/cli.py +39 -0
- bedrock_agentcore_starter_toolkit/cli/common.py +44 -0
- bedrock_agentcore_starter_toolkit/cli/gateway/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/cli/gateway/commands.py +88 -0
- bedrock_agentcore_starter_toolkit/cli/runtime/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +603 -0
- bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py +124 -0
- bedrock_agentcore_starter_toolkit/notebook/__init__.py +5 -0
- bedrock_agentcore_starter_toolkit/notebook/runtime/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py +196 -0
- bedrock_agentcore_starter_toolkit/operations/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/operations/gateway/README.md +277 -0
- bedrock_agentcore_starter_toolkit/operations/gateway/__init__.py +6 -0
- bedrock_agentcore_starter_toolkit/operations/gateway/client.py +456 -0
- bedrock_agentcore_starter_toolkit/operations/gateway/constants.py +152 -0
- bedrock_agentcore_starter_toolkit/operations/gateway/create_lambda.py +85 -0
- bedrock_agentcore_starter_toolkit/operations/gateway/create_role.py +89 -0
- bedrock_agentcore_starter_toolkit/operations/gateway/exceptions.py +13 -0
- bedrock_agentcore_starter_toolkit/operations/runtime/__init__.py +26 -0
- bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +227 -0
- bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py +129 -0
- bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +163 -0
- bedrock_agentcore_starter_toolkit/operations/runtime/models.py +76 -0
- bedrock_agentcore_starter_toolkit/operations/runtime/status.py +66 -0
- bedrock_agentcore_starter_toolkit/services/ecr.py +55 -0
- bedrock_agentcore_starter_toolkit/services/runtime.py +383 -0
- bedrock_agentcore_starter_toolkit/utils/endpoints.py +32 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/config.py +129 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/container.py +310 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/entrypoint.py +197 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/logs.py +33 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/schema.py +141 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2 +59 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/templates/dockerignore.template +69 -0
- bedrock_agentcore_starter_toolkit-0.1.0.dist-info/METADATA +137 -0
- bedrock_agentcore_starter_toolkit-0.1.0.dist-info/RECORD +41 -0
- bedrock_agentcore_starter_toolkit-0.1.0.dist-info/entry_points.txt +2 -0
- bedrock_agentcore_starter_toolkit-0.1.0.dist-info/licenses/NOTICE.txt +190 -0
- bedrock_agentcore_starter_toolkit/init.py +0 -3
- bedrock_agentcore_starter_toolkit-0.0.1.dist-info/METADATA +0 -26
- bedrock_agentcore_starter_toolkit-0.0.1.dist-info/RECORD +0 -5
- {bedrock_agentcore_starter_toolkit-0.0.1.dist-info → bedrock_agentcore_starter_toolkit-0.1.0.dist-info}/WHEEL +0 -0
- /bedrock_agentcore_starter_toolkit-0.0.1.dist-info/licenses/LICENSE → /bedrock_agentcore_starter_toolkit-0.1.0.dist-info/licenses/LICENSE.txt +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Pydantic models for operation requests and responses."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
7
|
+
|
|
8
|
+
from ...utils.runtime.container import ContainerRuntime
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Configure operation models
|
|
12
|
+
class ConfigureResult(BaseModel):
|
|
13
|
+
"""Result of configure operation."""
|
|
14
|
+
|
|
15
|
+
config_path: Path = Field(..., description="Path to configuration file")
|
|
16
|
+
dockerfile_path: Path = Field(..., description="Path to generated Dockerfile")
|
|
17
|
+
dockerignore_path: Optional[Path] = Field(None, description="Path to generated .dockerignore")
|
|
18
|
+
runtime: str = Field(..., description="Container runtime name")
|
|
19
|
+
region: str = Field(..., description="AWS region")
|
|
20
|
+
account_id: str = Field(..., description="AWS account ID")
|
|
21
|
+
execution_role: Optional[str] = Field(None, description="AWS execution role ARN")
|
|
22
|
+
ecr_repository: Optional[str] = Field(None, description="ECR repository URI")
|
|
23
|
+
auto_create_ecr: bool = Field(False, description="Whether ECR will be auto-created")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Launch operation models
|
|
27
|
+
class LaunchResult(BaseModel):
|
|
28
|
+
"""Result of launch operation."""
|
|
29
|
+
|
|
30
|
+
mode: str = Field(..., description="Launch mode: local, push-ecr, or cloud")
|
|
31
|
+
tag: str = Field(..., description="Docker image tag")
|
|
32
|
+
env_vars: Optional[Dict[str, str]] = Field(default=None, description="Environment variables for local deployment")
|
|
33
|
+
|
|
34
|
+
# Local mode fields
|
|
35
|
+
port: Optional[int] = Field(default=None, description="Port for local deployment")
|
|
36
|
+
runtime: Optional[ContainerRuntime] = Field(default=None, description="Container runtime instance")
|
|
37
|
+
|
|
38
|
+
# Cloud mode fields
|
|
39
|
+
ecr_uri: Optional[str] = Field(default=None, description="ECR repository URI")
|
|
40
|
+
agent_id: Optional[str] = Field(default=None, description="BedrockAgentCore agent ID")
|
|
41
|
+
agent_arn: Optional[str] = Field(default=None, description="BedrockAgentCore agent ARN")
|
|
42
|
+
|
|
43
|
+
# Build output (optional)
|
|
44
|
+
build_output: Optional[List[str]] = Field(default=None, description="Docker build output")
|
|
45
|
+
|
|
46
|
+
model_config = ConfigDict(arbitrary_types_allowed=True) # For runtime field
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class InvokeResult(BaseModel):
|
|
50
|
+
"""Result of invoke operation."""
|
|
51
|
+
|
|
52
|
+
response: Dict[str, Any] = Field(..., description="Response from Bedrock AgentCore endpoint")
|
|
53
|
+
session_id: str = Field(..., description="Session ID used for invocation")
|
|
54
|
+
agent_arn: Optional[str] = Field(default=None, description="BedrockAgentCore agent ARN")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Status operation models
|
|
58
|
+
class StatusConfigInfo(BaseModel):
|
|
59
|
+
"""Configuration information for status."""
|
|
60
|
+
|
|
61
|
+
name: str = Field(..., description="Bedrock AgentCore application name")
|
|
62
|
+
entrypoint: str = Field(..., description="Entrypoint file path")
|
|
63
|
+
region: Optional[str] = Field(None, description="AWS region")
|
|
64
|
+
account: Optional[str] = Field(None, description="AWS account ID")
|
|
65
|
+
execution_role: Optional[str] = Field(None, description="AWS execution role ARN")
|
|
66
|
+
ecr_repository: Optional[str] = Field(None, description="ECR repository URI")
|
|
67
|
+
agent_id: Optional[str] = Field(None, description="BedrockAgentCore agent ID")
|
|
68
|
+
agent_arn: Optional[str] = Field(None, description="BedrockAgentCore agent ARN")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class StatusResult(BaseModel):
|
|
72
|
+
"""Result of status operation."""
|
|
73
|
+
|
|
74
|
+
config: StatusConfigInfo = Field(..., description="Configuration information")
|
|
75
|
+
agent: Optional[Dict[str, Any]] = Field(None, description="Agent runtime details or error")
|
|
76
|
+
endpoint: Optional[Dict[str, Any]] = Field(None, description="Endpoint details or error")
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Status operations for Bedrock AgentCore SDK."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from ...services.runtime import BedrockAgentCoreClient
|
|
7
|
+
from ...utils.runtime.config import load_config
|
|
8
|
+
from .models import StatusConfigInfo, StatusResult
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_status(config_path: Path, agent_name: Optional[str] = None) -> StatusResult:
|
|
12
|
+
"""Get Bedrock AgentCore status including config and runtime details.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
config_path: Path to BedrockAgentCore configuration file
|
|
16
|
+
agent_name: Name of agent to get status for (for project configurations)
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
StatusResult with config, agent, and endpoint status
|
|
20
|
+
|
|
21
|
+
Raises:
|
|
22
|
+
FileNotFoundError: If configuration file doesn't exist
|
|
23
|
+
ValueError: If Bedrock AgentCore is not deployed or configuration is invalid
|
|
24
|
+
"""
|
|
25
|
+
# Load project configuration
|
|
26
|
+
project_config = load_config(config_path)
|
|
27
|
+
agent_config = project_config.get_agent_config(agent_name)
|
|
28
|
+
|
|
29
|
+
# Build config info
|
|
30
|
+
config_info = StatusConfigInfo(
|
|
31
|
+
name=agent_config.name,
|
|
32
|
+
entrypoint=agent_config.entrypoint,
|
|
33
|
+
region=agent_config.aws.region,
|
|
34
|
+
account=agent_config.aws.account,
|
|
35
|
+
execution_role=agent_config.aws.execution_role,
|
|
36
|
+
ecr_repository=agent_config.aws.ecr_repository,
|
|
37
|
+
agent_id=agent_config.bedrock_agentcore.agent_id,
|
|
38
|
+
agent_arn=agent_config.bedrock_agentcore.agent_arn,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Initialize status result
|
|
42
|
+
agent_details = None
|
|
43
|
+
endpoint_details = None
|
|
44
|
+
|
|
45
|
+
# If agent is deployed, get runtime status
|
|
46
|
+
if agent_config.bedrock_agentcore.agent_id and agent_config.aws.region:
|
|
47
|
+
try:
|
|
48
|
+
client = BedrockAgentCoreClient(agent_config.aws.region)
|
|
49
|
+
|
|
50
|
+
# Get agent runtime details
|
|
51
|
+
try:
|
|
52
|
+
agent_details = client.get_agent_runtime(agent_config.bedrock_agentcore.agent_id)
|
|
53
|
+
except Exception as e:
|
|
54
|
+
agent_details = {"error": str(e)}
|
|
55
|
+
|
|
56
|
+
# Get endpoint details
|
|
57
|
+
try:
|
|
58
|
+
endpoint_details = client.get_agent_runtime_endpoint(agent_config.bedrock_agentcore.agent_id)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
endpoint_details = {"error": str(e)}
|
|
61
|
+
|
|
62
|
+
except Exception as e:
|
|
63
|
+
agent_details = {"error": f"Failed to initialize Bedrock AgentCore client: {e}"}
|
|
64
|
+
endpoint_details = {"error": f"Failed to initialize Bedrock AgentCore client: {e}"}
|
|
65
|
+
|
|
66
|
+
return StatusResult(config=config_info, agent=agent_details, endpoint=endpoint_details)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""ECR (Elastic Container Registry) service integration."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
|
|
5
|
+
import boto3
|
|
6
|
+
|
|
7
|
+
from ..utils.runtime.container import ContainerRuntime
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_account_id() -> str:
|
|
11
|
+
"""Get AWS account ID."""
|
|
12
|
+
return boto3.client("sts").get_caller_identity()["Account"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_region() -> str:
|
|
16
|
+
"""Get AWS region."""
|
|
17
|
+
return boto3.Session().region_name or "us-west-2"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def create_ecr_repository(repo_name: str, region: str) -> str:
|
|
21
|
+
"""Create or get existing ECR repository."""
|
|
22
|
+
ecr = boto3.client("ecr", region_name=region)
|
|
23
|
+
try:
|
|
24
|
+
response = ecr.create_repository(repositoryName=repo_name)
|
|
25
|
+
return response["repository"]["repositoryUri"]
|
|
26
|
+
except ecr.exceptions.RepositoryAlreadyExistsException:
|
|
27
|
+
response = ecr.describe_repositories(repositoryNames=[repo_name])
|
|
28
|
+
return response["repositories"][0]["repositoryUri"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def deploy_to_ecr(local_tag: str, repo_name: str, region: str, container_runtime: ContainerRuntime) -> str:
|
|
32
|
+
"""Build and push image to ECR."""
|
|
33
|
+
ecr = boto3.client("ecr", region_name=region)
|
|
34
|
+
|
|
35
|
+
# Get or create repository
|
|
36
|
+
ecr_uri = create_ecr_repository(repo_name, region)
|
|
37
|
+
|
|
38
|
+
# Get auth token
|
|
39
|
+
auth_data = ecr.get_authorization_token()["authorizationData"][0]
|
|
40
|
+
token = base64.b64decode(auth_data["authorizationToken"]).decode("utf-8")
|
|
41
|
+
username, password = token.split(":")
|
|
42
|
+
|
|
43
|
+
# Login to ECR
|
|
44
|
+
if not container_runtime.login(auth_data["proxyEndpoint"], username, password):
|
|
45
|
+
raise RuntimeError("Failed to login to ECR")
|
|
46
|
+
|
|
47
|
+
# Tag and push
|
|
48
|
+
ecr_tag = f"{ecr_uri}:latest"
|
|
49
|
+
if not container_runtime.tag(local_tag, ecr_tag):
|
|
50
|
+
raise RuntimeError("Failed to tag image")
|
|
51
|
+
|
|
52
|
+
if not container_runtime.push(ecr_tag):
|
|
53
|
+
raise RuntimeError("Failed to push image to ECR")
|
|
54
|
+
|
|
55
|
+
return ecr_tag
|
|
@@ -0,0 +1,383 @@
|
|
|
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
|
+
|
|
13
|
+
from ..utils.endpoints import get_control_plane_endpoint, get_data_plane_endpoint
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def generate_session_id() -> str:
|
|
17
|
+
"""Generate session ID."""
|
|
18
|
+
return str(uuid.uuid4())
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _handle_http_response(response) -> dict:
|
|
22
|
+
response.raise_for_status()
|
|
23
|
+
if "text/event-stream" in response.headers.get("content-type", ""):
|
|
24
|
+
return _handle_streaming_response(response)
|
|
25
|
+
else:
|
|
26
|
+
# Check if response has content
|
|
27
|
+
if not response.content:
|
|
28
|
+
raise ValueError("Empty response from agent endpoint")
|
|
29
|
+
|
|
30
|
+
return {"response": response.text}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _handle_aws_response(response) -> dict:
|
|
34
|
+
if "text/event-stream" in response.get("contentType", ""):
|
|
35
|
+
return _handle_streaming_response(response["response"])
|
|
36
|
+
else:
|
|
37
|
+
try:
|
|
38
|
+
events = []
|
|
39
|
+
for event in response.get("response", []):
|
|
40
|
+
events.append(event)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
events = [f"Error reading EventStream: {e}"]
|
|
43
|
+
|
|
44
|
+
response["response"] = events
|
|
45
|
+
return response
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _handle_streaming_response(response) -> Dict[str, Any]:
|
|
49
|
+
logger = logging.getLogger("bedrock_agentcore.stream")
|
|
50
|
+
logger.setLevel(logging.INFO)
|
|
51
|
+
|
|
52
|
+
content = []
|
|
53
|
+
for line in response.iter_lines(chunk_size=1):
|
|
54
|
+
if line:
|
|
55
|
+
line = line.decode("utf-8")
|
|
56
|
+
if line.startswith("data: "):
|
|
57
|
+
line = line[6:]
|
|
58
|
+
logger.info(line)
|
|
59
|
+
content.append(line)
|
|
60
|
+
|
|
61
|
+
return {"response": "\n".join(content)}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class BedrockAgentCoreClient:
|
|
65
|
+
"""Bedrock AgentCore client for agent management."""
|
|
66
|
+
|
|
67
|
+
def __init__(self, region: str):
|
|
68
|
+
"""Initialize Bedrock AgentCore client.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
region: AWS region for the client
|
|
72
|
+
"""
|
|
73
|
+
self.region = region
|
|
74
|
+
self.logger = logging.getLogger(f"bedrock_agentcore.runtime.{region}")
|
|
75
|
+
|
|
76
|
+
# Get endpoint URLs and log them
|
|
77
|
+
control_plane_url = get_control_plane_endpoint(region)
|
|
78
|
+
data_plane_url = get_data_plane_endpoint(region)
|
|
79
|
+
|
|
80
|
+
self.logger.debug("Initializing Bedrock AgentCore client for region: %s", region)
|
|
81
|
+
self.logger.debug("Control plane: %s", control_plane_url)
|
|
82
|
+
self.logger.debug("Data plane: %s", data_plane_url)
|
|
83
|
+
|
|
84
|
+
self.client = boto3.client("bedrock-agentcore-control", region_name=region, endpoint_url=control_plane_url)
|
|
85
|
+
self.dataplane_client = boto3.client("bedrock-agentcore", region_name=region, endpoint_url=data_plane_url)
|
|
86
|
+
|
|
87
|
+
def create_agent(
|
|
88
|
+
self,
|
|
89
|
+
agent_name: str,
|
|
90
|
+
image_uri: str,
|
|
91
|
+
execution_role_arn: str,
|
|
92
|
+
network_config: Optional[Dict] = None,
|
|
93
|
+
authorizer_config: Optional[Dict] = None,
|
|
94
|
+
protocol_config: Optional[Dict] = None,
|
|
95
|
+
env_vars: Optional[Dict] = None,
|
|
96
|
+
) -> Dict[str, str]:
|
|
97
|
+
"""Create new agent."""
|
|
98
|
+
self.logger.info("Creating agent '%s' with image URI: %s", agent_name, image_uri)
|
|
99
|
+
try:
|
|
100
|
+
# Build parameters dict, only including optional configs when present
|
|
101
|
+
params = {
|
|
102
|
+
"agentRuntimeName": agent_name,
|
|
103
|
+
"agentRuntimeArtifact": {"containerConfiguration": {"containerUri": image_uri}},
|
|
104
|
+
"roleArn": execution_role_arn,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if network_config is not None:
|
|
108
|
+
params["networkConfiguration"] = network_config
|
|
109
|
+
|
|
110
|
+
if authorizer_config is not None:
|
|
111
|
+
params["authorizerConfiguration"] = authorizer_config
|
|
112
|
+
|
|
113
|
+
if protocol_config is not None:
|
|
114
|
+
params["protocolConfiguration"] = protocol_config
|
|
115
|
+
|
|
116
|
+
if env_vars is not None:
|
|
117
|
+
params["environmentVariables"] = env_vars
|
|
118
|
+
|
|
119
|
+
resp = self.client.create_agent_runtime(**params)
|
|
120
|
+
agent_id = resp["agentRuntimeId"]
|
|
121
|
+
agent_arn = resp["agentRuntimeArn"]
|
|
122
|
+
self.logger.info("Successfully created agent '%s' with ID: %s, ARN: %s", agent_name, agent_id, agent_arn)
|
|
123
|
+
return {"id": agent_id, "arn": agent_arn}
|
|
124
|
+
except Exception as e:
|
|
125
|
+
self.logger.error("Failed to create agent '%s': %s", agent_name, str(e))
|
|
126
|
+
raise
|
|
127
|
+
|
|
128
|
+
def update_agent(
|
|
129
|
+
self,
|
|
130
|
+
agent_id: str,
|
|
131
|
+
image_uri: str,
|
|
132
|
+
execution_role_arn: str,
|
|
133
|
+
network_config: Optional[Dict] = None,
|
|
134
|
+
authorizer_config: Optional[Dict] = None,
|
|
135
|
+
protocol_config: Optional[Dict] = None,
|
|
136
|
+
env_vars: Optional[Dict] = None,
|
|
137
|
+
) -> Dict[str, str]:
|
|
138
|
+
"""Update existing agent."""
|
|
139
|
+
self.logger.info("Updating agent ID '%s' with image URI: %s", agent_id, image_uri)
|
|
140
|
+
try:
|
|
141
|
+
# Build parameters dict, only including optional configs when present
|
|
142
|
+
params = {
|
|
143
|
+
"agentRuntimeId": agent_id,
|
|
144
|
+
"agentRuntimeArtifact": {"containerConfiguration": {"containerUri": image_uri}},
|
|
145
|
+
"roleArn": execution_role_arn,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if network_config is not None:
|
|
149
|
+
params["networkConfiguration"] = network_config
|
|
150
|
+
|
|
151
|
+
if authorizer_config is not None:
|
|
152
|
+
params["authorizerConfiguration"] = authorizer_config
|
|
153
|
+
|
|
154
|
+
if protocol_config is not None:
|
|
155
|
+
params["protocolConfiguration"] = protocol_config
|
|
156
|
+
|
|
157
|
+
if env_vars is not None:
|
|
158
|
+
params["environmentVariables"] = env_vars
|
|
159
|
+
|
|
160
|
+
resp = self.client.update_agent_runtime(**params)
|
|
161
|
+
agent_arn = resp["agentRuntimeArn"]
|
|
162
|
+
self.logger.info("Successfully updated agent ID '%s', ARN: %s", agent_id, agent_arn)
|
|
163
|
+
return {"id": agent_id, "arn": agent_arn}
|
|
164
|
+
except Exception as e:
|
|
165
|
+
self.logger.error("Failed to update agent ID '%s': %s", agent_id, str(e))
|
|
166
|
+
raise
|
|
167
|
+
|
|
168
|
+
def create_or_update_agent(
|
|
169
|
+
self,
|
|
170
|
+
agent_id: Optional[str],
|
|
171
|
+
agent_name: str,
|
|
172
|
+
image_uri: str,
|
|
173
|
+
execution_role_arn: str,
|
|
174
|
+
network_config: Optional[Dict] = None,
|
|
175
|
+
authorizer_config: Optional[Dict] = None,
|
|
176
|
+
protocol_config: Optional[Dict] = None,
|
|
177
|
+
env_vars: Optional[Dict] = None,
|
|
178
|
+
) -> Dict[str, str]:
|
|
179
|
+
"""Create or update agent."""
|
|
180
|
+
if agent_id:
|
|
181
|
+
return self.update_agent(
|
|
182
|
+
agent_id, image_uri, execution_role_arn, network_config, authorizer_config, protocol_config, env_vars
|
|
183
|
+
)
|
|
184
|
+
return self.create_agent(
|
|
185
|
+
agent_name, image_uri, execution_role_arn, network_config, authorizer_config, protocol_config, env_vars
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def wait_for_agent_endpoint_ready(self, agent_id: str, endpoint_name: str = "DEFAULT", max_wait: int = 120) -> str:
|
|
189
|
+
"""Wait for agent endpoint to be ready.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
agent_id: Agent ID to wait for
|
|
193
|
+
endpoint_name: Endpoint name, defaults to "DEFAULT"
|
|
194
|
+
max_wait: Maximum wait time in seconds
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
Agent endpoint ARN when ready
|
|
198
|
+
"""
|
|
199
|
+
start_time = time.time()
|
|
200
|
+
|
|
201
|
+
while time.time() - start_time < max_wait:
|
|
202
|
+
try:
|
|
203
|
+
resp = self.client.get_agent_runtime_endpoint(
|
|
204
|
+
agentRuntimeId=agent_id,
|
|
205
|
+
endpointName=endpoint_name,
|
|
206
|
+
)
|
|
207
|
+
status = resp.get("status", "UNKNOWN")
|
|
208
|
+
|
|
209
|
+
if status == "READY":
|
|
210
|
+
return resp["agentRuntimeEndpointArn"]
|
|
211
|
+
elif status in ["CREATE_FAILED", "UPDATE_FAILED"]:
|
|
212
|
+
raise Exception(
|
|
213
|
+
f"Agent endpoint {status.lower().replace('_', ' ')}: {resp.get('failureReason', 'Unknown')}"
|
|
214
|
+
)
|
|
215
|
+
elif status not in ["CREATING", "UPDATING"]:
|
|
216
|
+
pass
|
|
217
|
+
except self.client.exceptions.ResourceNotFoundException:
|
|
218
|
+
pass
|
|
219
|
+
except Exception as e:
|
|
220
|
+
if "ResourceNotFoundException" not in str(e):
|
|
221
|
+
raise
|
|
222
|
+
time.sleep(2)
|
|
223
|
+
return (
|
|
224
|
+
f"Endpoint is taking longer than {max_wait} seconds to be ready, "
|
|
225
|
+
f"please check status and try to invoke after some time"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
def get_agent_runtime(self, agent_id: str) -> Dict:
|
|
229
|
+
"""Get agent runtime details.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
agent_id: Agent ID to get details for
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
Agent runtime details
|
|
236
|
+
"""
|
|
237
|
+
return self.client.get_agent_runtime(agentRuntimeId=agent_id)
|
|
238
|
+
|
|
239
|
+
def get_agent_runtime_endpoint(self, agent_id: str, endpoint_name: str = "DEFAULT") -> Dict:
|
|
240
|
+
"""Get agent runtime endpoint details.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
agent_id: Agent ID to get endpoint for
|
|
244
|
+
endpoint_name: Endpoint name, defaults to "DEFAULT"
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Agent endpoint details
|
|
248
|
+
"""
|
|
249
|
+
return self.client.get_agent_runtime_endpoint(
|
|
250
|
+
agentRuntimeId=agent_id,
|
|
251
|
+
endpointName=endpoint_name,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
def invoke_endpoint(
|
|
255
|
+
self,
|
|
256
|
+
agent_arn: str,
|
|
257
|
+
payload: str,
|
|
258
|
+
session_id: str,
|
|
259
|
+
endpoint_name: str = "DEFAULT",
|
|
260
|
+
user_id: Optional[str] = None,
|
|
261
|
+
) -> Dict:
|
|
262
|
+
"""Invoke agent endpoint."""
|
|
263
|
+
req = {
|
|
264
|
+
"agentRuntimeArn": agent_arn,
|
|
265
|
+
"qualifier": endpoint_name,
|
|
266
|
+
"runtimeSessionId": session_id,
|
|
267
|
+
"payload": payload,
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if user_id:
|
|
271
|
+
req["runtimeUserId"] = user_id
|
|
272
|
+
|
|
273
|
+
response = self.dataplane_client.invoke_agent_runtime(**req)
|
|
274
|
+
return _handle_aws_response(response)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class HttpBedrockAgentCoreClient:
|
|
278
|
+
"""Bedrock AgentCore client for agent management using HTTP requests with bearer token."""
|
|
279
|
+
|
|
280
|
+
def __init__(self, region: str):
|
|
281
|
+
"""Initialize HttpBedrockAgentCoreClient.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
region: AWS region for the client
|
|
285
|
+
"""
|
|
286
|
+
self.region = region
|
|
287
|
+
self.dp_endpoint = get_data_plane_endpoint(region)
|
|
288
|
+
self.logger = logging.getLogger(f"bedrock_agentcore.http_runtime.{region}")
|
|
289
|
+
|
|
290
|
+
self.logger.debug("Initializing HTTP Bedrock AgentCore client for region: %s", region)
|
|
291
|
+
self.logger.debug("Data plane: %s", self.dp_endpoint)
|
|
292
|
+
|
|
293
|
+
def invoke_endpoint(
|
|
294
|
+
self,
|
|
295
|
+
agent_arn: str,
|
|
296
|
+
payload,
|
|
297
|
+
session_id: str,
|
|
298
|
+
bearer_token: Optional[str],
|
|
299
|
+
endpoint_name: str = "DEFAULT",
|
|
300
|
+
) -> Dict:
|
|
301
|
+
"""Invoke agent endpoint using HTTP request with bearer token.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
agent_arn: Agent ARN to invoke
|
|
305
|
+
payload: Payload to send (dict or string)
|
|
306
|
+
session_id: Session ID for the request
|
|
307
|
+
bearer_token: Bearer token for authentication
|
|
308
|
+
endpoint_name: Endpoint name, defaults to "DEFAULT"
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
Response from the agent endpoint
|
|
312
|
+
"""
|
|
313
|
+
# Escape agent ARN for URL
|
|
314
|
+
escaped_arn = urllib.parse.quote(agent_arn, safe="")
|
|
315
|
+
|
|
316
|
+
# Build URL
|
|
317
|
+
url = f"{self.dp_endpoint}/runtimes/{escaped_arn}/invocations"
|
|
318
|
+
# Headers
|
|
319
|
+
headers = {
|
|
320
|
+
"Authorization": f"Bearer {bearer_token}",
|
|
321
|
+
"Content-Type": "application/json",
|
|
322
|
+
"X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": session_id,
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
# Parse the payload string back to JSON object to send properly
|
|
326
|
+
# This ensures consistent payload structure between boto3 and HTTP clients
|
|
327
|
+
try:
|
|
328
|
+
body = json.loads(payload) if isinstance(payload, str) else payload
|
|
329
|
+
except json.JSONDecodeError:
|
|
330
|
+
# Fallback for non-JSON strings - wrap in payload object
|
|
331
|
+
self.logger.warning("Failed to parse payload as JSON, wrapping in payload object")
|
|
332
|
+
body = {"payload": payload}
|
|
333
|
+
|
|
334
|
+
try:
|
|
335
|
+
# Make request with timeout
|
|
336
|
+
response = requests.post(
|
|
337
|
+
url,
|
|
338
|
+
params={"qualifier": endpoint_name},
|
|
339
|
+
headers=headers,
|
|
340
|
+
json=body,
|
|
341
|
+
timeout=100,
|
|
342
|
+
stream=True,
|
|
343
|
+
)
|
|
344
|
+
return _handle_http_response(response)
|
|
345
|
+
except requests.exceptions.RequestException as e:
|
|
346
|
+
self.logger.error("Failed to invoke agent endpoint: %s", str(e))
|
|
347
|
+
raise
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class LocalBedrockAgentCoreClient:
|
|
351
|
+
"""Local Bedrock AgentCore client for invoking endpoints."""
|
|
352
|
+
|
|
353
|
+
def __init__(self, endpoint: str):
|
|
354
|
+
"""Initialize the local client with the given endpoint."""
|
|
355
|
+
self.endpoint = endpoint
|
|
356
|
+
self.logger = logging.getLogger("bedrock_agentcore.http_local")
|
|
357
|
+
|
|
358
|
+
def invoke_endpoint(self, session_id: str, payload: str, workload_access_token: str):
|
|
359
|
+
"""Invoke the endpoint with the given parameters."""
|
|
360
|
+
from bedrock_agentcore.runtime.models import ACCESS_TOKEN_HEADER, SESSION_HEADER
|
|
361
|
+
|
|
362
|
+
url = f"{self.endpoint}/invocations"
|
|
363
|
+
|
|
364
|
+
headers = {
|
|
365
|
+
"Content-Type": "application/json",
|
|
366
|
+
ACCESS_TOKEN_HEADER: workload_access_token,
|
|
367
|
+
SESSION_HEADER: session_id,
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
try:
|
|
371
|
+
body = json.loads(payload) if isinstance(payload, str) else payload
|
|
372
|
+
except json.JSONDecodeError:
|
|
373
|
+
# Fallback for non-JSON strings - wrap in payload object
|
|
374
|
+
self.logger.warning("Failed to parse payload as JSON, wrapping in payload object")
|
|
375
|
+
body = {"payload": payload}
|
|
376
|
+
|
|
377
|
+
try:
|
|
378
|
+
# Make request with timeout
|
|
379
|
+
response = requests.post(url, headers=headers, json=body, timeout=100, stream=True)
|
|
380
|
+
return _handle_http_response(response)
|
|
381
|
+
except requests.exceptions.RequestException as e:
|
|
382
|
+
self.logger.error("Failed to invoke agent endpoint: %s", str(e))
|
|
383
|
+
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"
|