From af75926b29421b74871688e21934dbee01cfb822 Mon Sep 17 00:00:00 2001 From: Bharathi Srinivasan Date: Wed, 29 Apr 2026 06:21:59 -0700 Subject: [PATCH] adding CLI examples and removing starter toolkit --- .../01-strands-with-bedrock-model/README.md | 119 +- .../requirements.txt | 3 +- ...time_with_strands_and_bedrock_models.ipynb | 327 +++-- .../02-langgraph-with-bedrock-model/README.md | 108 +- .../requirements.txt | 3 +- ...me_with_langgraph_and_bedrock_models.ipynb | 321 +++-- .../03-strands-with-openai-model/README.md | 115 +- .../requirements.txt | 3 +- ...ntime_with_strands_and_openai_models.ipynb | 315 +++-- .../research_crew/requirements.txt | 2 +- ...ntime-with-crewai-and-bedrock-models.ipynb | 290 +++-- .../06-strands-with-skills/README.md | 121 +- .../06-strands-with-skills/requirements.txt | 3 +- .../runtime_with_strands_and_skills.ipynb | 1138 +++++++++-------- .../01-hosting-agent/README.md | 83 +- .../hosting_mcp_server.ipynb | 194 +-- .../hosting_mcp_server_iam_auth.ipynb | 176 ++- .../02-hosting-MCP-server/requirements.txt | 3 +- .../requirements.txt | 3 +- .../streamining_agent_response.ipynb | 195 ++- .../requirements.txt | 3 +- .../understanding_runtime_context.ipynb | 283 ++-- .../handling_large_payloads.ipynb | 198 +-- .../requirements.txt | 3 +- .../agent/requirements.txt | 1 - .../weekly_update_agentcore_deploy.ipynb | 236 ++-- .../async_data_analysis_tutorial.ipynb | 178 ++- .../02_async_data_analysis/requirements.txt | 3 +- .../distributed_agents_with_agentcore.ipynb | 218 ++-- .../hr_agent/requirements.txt | 1 - .../orchestrator_agent/requirements.txt | 1 - .../requirements.txt | 1 - .../tech_agent/requirements.txt | 1 - .../middleware_support.ipynb | 178 ++- .../06-middleware-support/requirements.txt | 3 +- .../agentcore-runtime-in-vpc-access.ipynb | 121 +- ...2a-getting-started-agentcore-strands.ipynb | 233 +++- .../02-a2a-deploy-orchestrator.ipynb | 150 ++- .../03-a2a-cleanup.ipynb | 75 +- .../requirements.txt | 3 +- .../02-a2a-agent-sigv4/deploy.py | 1 - .../hosting_a2a_iam_auth.ipynb | 73 +- .../02-a2a-agent-sigv4/requirements.txt | 3 +- .../02-strands-ws/websocket/requirements.txt | 2 +- .../utils/cleanup.py | 3 +- .../utils/deploy.py | 2 - .../utils/requirements.txt | 3 +- .../deploy_dcr_mcp_agentcore.ipynb | 141 +- .../requirements.txt | 1 - .../mcp_server_features_e2e.ipynb | 165 ++- .../08-mcp-e2e/01-server-e2e/requirements.txt | 1 - .../mcp_client_features_e2e.ipynb | 143 ++- .../08-mcp-e2e/02-client-e2e/requirements.txt | 1 - .../03-utilities-e2e/01_progress.ipynb | 147 ++- .../03-utilities-e2e/requirements.txt | 1 - .../09-execute-command/01_exec_command.ipynb | 185 ++- .../09-execute-command/requirements.txt | 1 - .../hosting_agui_agent_cognito.ipynb | 148 ++- .../hosting_agui_agent_iam.ipynb | 140 +- 01-tutorials/01-AgentCore-runtime/README.md | 121 +- 60 files changed, 4598 insertions(+), 2096 deletions(-) diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/01-strands-with-bedrock-model/README.md b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/01-strands-with-bedrock-model/README.md index d7b415552..3eb46642f 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/01-strands-with-bedrock-model/README.md +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/01-strands-with-bedrock-model/README.md @@ -2,7 +2,7 @@ ## Overview -In this tutorial we will learn how to host your existing agent, using Amazon Bedrock AgentCore Runtime. +In this tutorial we will learn how to host your existing agent, using Amazon Bedrock AgentCore Runtime. We will focus on a Strands Agents with Amazon Bedrock model example. For LangGraph with Amazon Bedrock model check [here](../02-langgraph-with-bedrock-model) and for a Strands Agents with an OpenAI model check [here](../03-strands-with-openai-model). @@ -21,20 +21,129 @@ and for a Strands Agents with an OpenAI model check [here](../03-strands-with-op | Example complexity | Easy | | SDK used | Amazon BedrockAgentCore Python SDK and boto3 | -### Tutorial Architecture +--- -In this tutorial we will describe how to deploy an existing agent to AgentCore runtime. +## Architecture -For demonstration purposes, we will use a Strands Agent using Amazon Bedrock models +When hosting agents, the SDK automatically: -In our example we will use a very simple agent with two tools: `get_weather` and `get_time`. +- Hosts your agent on port `8080` +- Provides two key endpoints: + - **`/invocations`**: Primary agent interaction (JSON input → JSON/SSE output) + - **`/ping`**: Health check for monitoring
+Once deployed, your AgentCore Runtime receives requests from clients via `invoke_agent_runtime` (boto3 or AWS SDK). Each session runs in an isolated microVM. The lifecycle looks as following: + +1. **Local experimentation** — run your agent as a Python script +2. **Wrap with SDK** — add `BedrockAgentCoreApp` decorator +3. **Deploy to Runtime** — package code, upload to S3, create runtime via boto3 +4. **Invoke** — call via boto3 `invoke_agent_runtime` + +--- + +## What's This Feature + +Amazon Bedrock AgentCore Runtime lets you deploy any Python-based agent (Strands, LangGraph, CrewAI, etc.) as a scalable, serverless HTTP service. You wrap your agent's invocation function with the `@app.entrypoint` decorator from the `bedrock_agentcore` SDK — no other code changes required. + ### Tutorial Key Features * Hosting Agents on Amazon Bedrock AgentCore Runtime * Using Amazon Bedrock models * Using Strands Agents +* CodeZip deployment (no Docker required) via boto3 `create_agent_runtime` +* Session lifecycle management with `stop_runtime_session` +* Lifecycle configuration (idle session timeout) via `update_agent_runtime` + +--- + +## CLI Commands + +> **CLI version**: `agentcore@0.11.0` +> +> Install or update: `npm install -g @aws/agentcore@0.11.0` + +The `agentcore` CLI provides an alternative end-to-end workflow for creating, deploying, invoking, and monitoring your runtime without writing deployment code. + +### 1. Create a new project + +```bash +agentcore create \ + --name stransclaude \ + --framework Strands \ + --model-provider Bedrock \ + --build CodeZip \ + --skip-git \ + --skip-install \ + --json +``` + +This scaffolds a project with `agentcore/agentcore.json` config, CDK stack, and a starter `main.py`. + +### 2. Replace the generated agent code + +Copy your agent code (`strands_claude.py`) into the project: + +```bash +cp strands_claude.py app/stransclaude/main.py +cp requirements.txt app/stransclaude/requirements.txt +``` + +### 3. Deploy to AgentCore Runtime + +```bash +cd stransclaude +agentcore deploy -y --json +``` + +This creates the IAM role, packages the code, and calls `create_agent_runtime`. Wait for `READY` status. + +### 4. Check deployment status + +```bash +agentcore status --json +``` + +### 5. Invoke the deployed agent + +```bash +agentcore invoke "What is the weather in Athens?" --json +``` + +### 6. View logs + +```bash +agentcore logs --since 30m -n 50 +``` + +--- + +## Cleanup + +To delete the runtime and associated resources: + +**Using boto3** (from the notebook cleanup cell): + +```python +import boto3 +agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region) +agentcore_control.delete_agent_runtime(agentRuntimeId=agent_runtime_id) +``` + +**Using CLI** (if deployed via `agentcore deploy`): + +```bash +# Remove the agent from project config and redeploy to tear down +agentcore remove agent --name stransclaude --json +agentcore deploy -y --json +``` + +Also delete the S3 deployment artifact: + +```python +s3 = boto3.client('s3', region_name=region) +s3.delete_object(Bucket=bucket_name, Key=s3_key) +``` diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/01-strands-with-bedrock-model/requirements.txt b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/01-strands-with-bedrock-model/requirements.txt index e0b1cf67c..90ebb49ad 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/01-strands-with-bedrock-model/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/01-strands-with-bedrock-model/requirements.txt @@ -2,5 +2,4 @@ strands-agents strands-agents-tools uv boto3 -bedrock-agentcore<=0.1.5 -bedrock-agentcore-starter-toolkit==0.1.14 \ No newline at end of file +bedrock-agentcore diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/01-strands-with-bedrock-model/runtime_with_strands_and_bedrock_models.ipynb b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/01-strands-with-bedrock-model/runtime_with_strands_and_bedrock_models.ipynb index 6059a5126..49688c9cb 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/01-strands-with-bedrock-model/runtime_with_strands_and_bedrock_models.ipynb +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/01-strands-with-bedrock-model/runtime_with_strands_and_bedrock_models.ipynb @@ -292,7 +292,7 @@ "source": [ "### Configure AgentCore Runtime deployment\n", "\n", - "First we will use our starter toolkit to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the starter kit to auto create the Amazon ECR repository on launch.\n", + "First we will use the AgentCore SDK and boto3 to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the SDK to auto create the Amazon ECR repository on launch.\n", "\n", "During the configure step, your docker file will be generated based on your application code\n", "\n", @@ -308,22 +308,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "from boto3.session import Session\n", - "boto_session = Session()\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", + "\n", + "boto_session = boto3.session.Session()\n", "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", "\n", - "agentcore_runtime = Runtime()\n", "agent_name = \"strands_claude_getting_started\"\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"strands_claude.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=agent_name\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"strands_claude.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "response" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -347,7 +425,24 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result = agentcore_runtime.launch()" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['strands_claude.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -367,15 +462,14 @@ "outputs": [], "source": [ "import time\n", - "status_response = agentcore_runtime.status()\n", - "status = status_response.endpoint['status']\n", "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", + "status = create_response.get('status', 'CREATING')\n", "while status not in end_status:\n", - " time.sleep(10)\n", - " status_response = agentcore_runtime.status()\n", - " status = status_response.endpoint['status']\n", - " print(status)\n", - "status" + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" ] }, { @@ -399,8 +493,30 @@ "metadata": {}, "outputs": [], "source": [ - "invoke_response = agentcore_runtime.invoke({\"prompt\": \"How is the weather now in Athens ?\"})\n", - "invoke_response" + "import boto3\n", + "import json\n", + "from IPython.display import Markdown, display\n", + "\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", + "\n", + "invoke_response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"How is the weather now in Athens ?\"})\n", + ")\n", + "\n", + "# Capture the runtime session ID for lifecycle management\n", + "runtime_session_id = invoke_response.get('runtimeSessionId')\n", + "print(f\"Runtime Session ID: {runtime_session_id}\")\n", + "\n", + "try:\n", + " events = []\n", + " for event in invoke_response.get(\"response\", []):\n", + " events.append(event)\n", + "except Exception as e:\n", + " events = [f\"Error reading EventStream: {e}\"]\n", + "response_text = json.loads(events[0].decode(\"utf-8\"))\n", + "display(Markdown(response_text))\n" ] }, { @@ -533,60 +649,68 @@ "outputs": [], "source": [ "# --- Lifecycle Configuration Demo ---\n", - "# In production, choose a timeout appropriate for your workload:\n", - "# - Development/testing: 5-15 minutes\n", - "# - Interactive sessions: 30-60 minutes\n", - "# - Long-running workloads: adjust as needed\n", - "#\n", + "# Create a second runtime with a 5-minute (300 second) idle timeout\n", + "# to show how lifecycle configuration affects session behavior.\n", "\n", - "agentcore_runtime_short = Runtime()\n", "agent_name_short = \"strands_claude_short_timeout\"\n", "\n", - "# Configure with shorter idle timeout\n", - "response_short = agentcore_runtime_short.configure(\n", - " entrypoint=\"strands_claude.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=agent_name_short\n", + "# Create IAM execution role for the short-timeout runtime\n", + "role_arn_short = create_or_get_execution_role(agent_name_short, region, account_id)\n", + "\n", + "# Package and upload (reuses existing S3 bucket)\n", + "bucket_name_short, s3_key_short = package_and_upload_to_s3(\n", + " agent_name_short,\n", + " [\"strands_claude.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", "\n", - "# Launch the second runtime\n", - "launch_result_short = agentcore_runtime_short.launch()\n", - "print(f\"Second runtime launched: {launch_result_short.agent_id}\")\n", + "# Create the second AgentCore Runtime\n", + "create_response_short = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name_short,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name_short, 'prefix': s3_key_short}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['strands_claude.py']\n", + " }\n", + " },\n", + " roleArn=role_arn_short,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id_short = create_response_short['agentRuntimeId']\n", + "agent_runtime_arn_short = create_response_short['agentRuntimeArn']\n", + "print(f\"Second runtime launched: {agent_runtime_id_short}\")\n", "\n", - "# Wait for it to be ready\n", - "status_response_short = agentcore_runtime_short.status()\n", - "status_short = status_response_short.endpoint['status']\n", + "# Wait for second runtime to be ready\n", + "status_short = create_response_short.get('status', 'CREATING')\n", "while status_short not in ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']:\n", - " time.sleep(10)\n", - " status_response_short = agentcore_runtime_short.status()\n", - " status_short = status_response_short.endpoint['status']\n", + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id_short)\n", + " status_short = r['status']\n", " print(f\"Short timeout runtime status: {status_short}\")\n", "\n", - "# Now update the runtime with shorter idle timeout using boto3\n", - "# UpdateAgentRuntime is a full-replacement API — we must re-supply all required fields.\n", - "# First, retrieve the current runtime configuration.\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", - "current_runtime = agentcore_control_client.get_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id\n", - ")\n", - "\n", - "update_response = agentcore_control_client.update_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id,\n", + "# Update the second runtime with a 5-minute idle timeout\n", + "# UpdateAgentRuntime is a full-replacement API — re-supply all required fields\n", + "current_runtime = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id_short)\n", + "update_response = agentcore_control.update_agent_runtime(\n", + " agentRuntimeId=agent_runtime_id_short,\n", " agentRuntimeArtifact=current_runtime['agentRuntimeArtifact'],\n", " roleArn=current_runtime['roleArn'],\n", " networkConfiguration=current_runtime['networkConfiguration'],\n", - " lifecycleConfiguration={\n", - " 'idleRuntimeSessionTimeout': 300 # 5 minutes\n", - " }\n", + " lifecycleConfiguration={'idleRuntimeSessionTimeout': 300} # 5 minutes\n", ")\n", - "print(f\"✅ Runtime updated with 5-minute idle timeout\")\n", + "print(f\"Second runtime updated with 5-minute idle timeout\")\n", "\n", "# Invoke the second runtime to verify it works\n", - "invoke_response_short = agentcore_runtime_short.invoke({\"prompt\": \"What is 3+3?\"})\n", - "print(f\"Second runtime response: {invoke_response_short['response'][0]}\")" + "invoke_short = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn_short,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"What is 3+3?\"})\n", + ")\n", + "short_events = [e for e in invoke_short.get(\"response\", [])]\n", + "if short_events:\n", + " print(f\"Second runtime response: {json.loads(short_events[0].decode('utf-8'))}\")\n" ] }, { @@ -621,7 +745,9 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result.ecr_uri, launch_result.agent_id, launch_result.ecr_uri.split('/')[1]" + "# Runtime info\n", + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -631,75 +757,50 @@ "metadata": {}, "outputs": [], "source": [ - "# --- Stop active sessions to release microVM resources ---\n", + "# --- Cleanup: Delete runtimes and S3 artifacts ---\n", "import boto3\n", "\n", "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", - "ecr_client = boto3.client('ecr', region_name=region)\n", - "\n", - "# Stop the active session to release its microVM resources\n", - "# In production, this is how you end individual user sessions while keeping the runtime alive\n", - "# AgentCore Runtime costs are based on vCPU and Memory — stopping sessions avoids undesired costs\n", - "# Note: If the session was already stopped in the earlier demo cell, this will raise a\n", - "# ResourceNotFoundException — the except block handles that gracefully.\n", + "s3_client = boto3.client('s3', region_name=region)\n", + "\n", + "# Stop the active session to release microVM resources\n", "if 'runtime_session_id' in locals() and runtime_session_id:\n", " try:\n", " agentcore_client.stop_runtime_session(\n", - " agentRuntimeArn=launch_result.agent_arn,\n", + " agentRuntimeArn=agent_runtime_arn,\n", " runtimeSessionId=runtime_session_id,\n", " qualifier='DEFAULT'\n", " )\n", - " print(f\"✅ Session '{runtime_session_id}' stopped\")\n", + " print(f\"Session '{runtime_session_id}' stopped\")\n", " except Exception as e:\n", - " print(f\"⚠️ Failed to stop session '{runtime_session_id}': {e}\")\n", + " print(f\"Could not stop session: {e}\")\n", "\n", - "# --- Delete both runtimes ---\n", - "# Original runtime\n", + "# Delete original runtime\n", "try:\n", - " agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result.agent_id,\n", - " )\n", - " print(f\"✅ Original runtime '{launch_result.agent_id}' deleted\")\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " print(f\"Runtime '{agent_name}' deleted\")\n", "except Exception as e:\n", - " print(f\"⚠️ Failed to delete original runtime: {e}\")\n", + " print(f\"Could not delete runtime: {e}\")\n", "\n", - "# Short-timeout runtime\n", - "if 'launch_result_short' in locals():\n", + "# Delete short-timeout runtime\n", + "if 'agent_runtime_id_short' in dir():\n", " try:\n", - " agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id,\n", - " )\n", - " print(f\"✅ Short-timeout runtime '{launch_result_short.agent_id}' deleted\")\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=agent_runtime_id_short)\n", + " print(f\"Short-timeout runtime deleted\")\n", " except Exception as e:\n", - " print(f\"⚠️ Failed to delete short-timeout runtime: {e}\")\n", + " print(f\"Could not delete short-timeout runtime: {e}\")\n", "\n", - "# --- Delete ECR repositories ---\n", + "# Delete S3 deployment artifacts\n", "try:\n", - " ecr_client.delete_repository(\n", - " repositoryName=launch_result.ecr_uri.split('/')[1],\n", - " force=True\n", - " )\n", - " print(f\"✅ ECR repository '{launch_result.ecr_uri.split('/')[1]}' deleted\")\n", + " s3_client.delete_object(Bucket=bucket_name, Key=s3_key)\n", + " print(f\"S3 artifact deleted: s3://{bucket_name}/{s3_key}\")\n", "except Exception as e:\n", - " print(f\"⚠️ Failed to delete ECR repository: {e}\")\n", - "\n", - "if 'launch_result_short' in locals():\n", - " try:\n", - " ecr_client.delete_repository(\n", - " repositoryName=launch_result_short.ecr_uri.split('/')[1],\n", - " force=True\n", - " )\n", - " print(f\"✅ Second ECR repository '{launch_result_short.ecr_uri.split('/')[1]}' deleted\")\n", - " except Exception as e:\n", - " print(f\"⚠️ Failed to delete second ECR repository: {e}\")\n", + " print(f\"Could not delete S3 artifact: {e}\")\n", "\n", - "# --- Delete local file to allow for consequtive executions of the notebook ---\n", - "try:\n", - " !rm .bedrock_agentcore.yaml\n", - " print(f\"✅ .bedrock_agentcore.yaml deleted\")\n", - "except Exception as e:\n", - " print(f\"⚠️ Failed to delete .bedrock_agentcore.yaml: {e}\")" + "# Delete local config file if it exists\n", + "import subprocess\n", + "subprocess.run([\"rm\", \"-f\", \".bedrock_agentcore.yaml\"], capture_output=True)\n", + "print(\"Cleanup complete.\")\n" ] }, { @@ -732,4 +833,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/02-langgraph-with-bedrock-model/README.md b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/02-langgraph-with-bedrock-model/README.md index fae7db607..d8064d664 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/02-langgraph-with-bedrock-model/README.md +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/02-langgraph-with-bedrock-model/README.md @@ -2,7 +2,7 @@ ## Overview -In this tutorial we will learn how to host your existing agent, using Amazon Bedrock AgentCore Runtime. +In this tutorial we will learn how to host your existing agent, using Amazon Bedrock AgentCore Runtime. We will focus on a LangGraph with Amazon Bedrock model example. For Strands Agents with Amazon Bedrock model check [here](../01-strands-with-bedrock-model) and for a Strands Agents with an OpenAI model check [here](../03-strands-with-openai-model). @@ -20,20 +20,118 @@ and for a Strands Agents with an OpenAI model check [here](../03-strands-with-op | Example complexity | Easy | | SDK used | Amazon BedrockAgentCore Python SDK and boto3 | -### Tutorial Architecture +--- -In this tutorial we will describe how to deploy an existing agent to AgentCore runtime. +## Architecture -For demonstration purposes, we will use a LangGraph agent using Amazon Bedrock models +When hosting agents, the SDK automatically: -In our example we will use a very simple agent with two tools: `get_weather` and `get_time`. +- Hosts your agent on port `8080` +- Provides two key endpoints: + - **`/invocations`**: Primary agent interaction (JSON input → JSON/SSE output) + - **`/ping`**: Health check for monitoring
+Once deployed, your AgentCore Runtime receives requests from clients via `invoke_agent_runtime`. Each session runs in an isolated microVM. The lifecycle: + +1. **Local experimentation** — run your agent as a Python script +2. **Wrap with SDK** — add `BedrockAgentCoreApp` decorator +3. **Deploy to Runtime** — package code, upload to S3, create runtime via boto3 +4. **Invoke** — call via boto3 `invoke_agent_runtime` + +--- + +## What's This Feature + +Amazon Bedrock AgentCore Runtime lets you deploy any Python-based agent (LangGraph, Strands, CrewAI, etc.) as a scalable, serverless HTTP service. You wrap your agent's invocation function with the `@app.entrypoint` decorator from the `bedrock_agentcore` SDK — no other code changes required. + ### Tutorial Key Features * Hosting Agents on Amazon Bedrock AgentCore Runtime * Using Amazon Bedrock models * Using LangGraph +* CodeZip deployment (no Docker required) via boto3 `create_agent_runtime` +* Session lifecycle management with `stop_runtime_session` +* Lifecycle configuration (idle session timeout) via `update_agent_runtime` + +--- + +## CLI Commands + +> **CLI version**: `agentcore@0.11.0` +> +> Install or update: `npm install -g @aws/agentcore@0.11.0` + +### 1. Create a new project + +```bash +agentcore create \ + --name langgraphclaude \ + --framework LangChain_LangGraph \ + --model-provider Bedrock \ + --build CodeZip \ + --skip-git \ + --skip-install \ + --json +``` + +### 2. Replace the generated agent code + +```bash +cp langgraph_bedrock.py app/langgraphclaude/main.py +cp requirements.txt app/langgraphclaude/requirements.txt +``` + +### 3. Deploy to AgentCore Runtime + +```bash +cd langgraphclaude +agentcore deploy -y --json +``` + +### 4. Check deployment status + +```bash +agentcore status --json +``` + +### 5. Invoke the deployed agent + +```bash +agentcore invoke "How much is 2+2?" --json +``` + +### 6. View logs + +```bash +agentcore logs --since 30m -n 50 +``` + +--- + +## Cleanup + +**Using boto3** (from the notebook cleanup cell): + +```python +import boto3 +agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region) +agentcore_control.delete_agent_runtime(agentRuntimeId=agent_runtime_id) +``` + +**Using CLI** (if deployed via `agentcore deploy`): + +```bash +agentcore remove agent --name langgraphclaude --json +agentcore deploy -y --json +``` + +Also delete the S3 deployment artifact: + +```python +s3 = boto3.client('s3', region_name=region) +s3.delete_object(Bucket=bucket_name, Key=s3_key) +``` diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/02-langgraph-with-bedrock-model/requirements.txt b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/02-langgraph-with-bedrock-model/requirements.txt index 04be5c91b..111b5c864 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/02-langgraph-with-bedrock-model/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/02-langgraph-with-bedrock-model/requirements.txt @@ -7,5 +7,4 @@ langchain-community opentelemetry-instrumentation-langchain starlette uvicorn -bedrock-agentcore<=0.1.5 -bedrock-agentcore-starter-toolkit==0.1.14 \ No newline at end of file +bedrock-agentcore diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/02-langgraph-with-bedrock-model/runtime_with_langgraph_and_bedrock_models.ipynb b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/02-langgraph-with-bedrock-model/runtime_with_langgraph_and_bedrock_models.ipynb index 31be04c20..efbf9f1c6 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/02-langgraph-with-bedrock-model/runtime_with_langgraph_and_bedrock_models.ipynb +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/02-langgraph-with-bedrock-model/runtime_with_langgraph_and_bedrock_models.ipynb @@ -442,7 +442,7 @@ "source": [ "### Configure AgentCore Runtime deployment\n", "\n", - "First we will use our starter toolkit to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the starter kit to auto create the Amazon ECR repository on launch.\n", + "First we will use the AgentCore SDK and boto3 to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the SDK to auto create the Amazon ECR repository on launch.\n", "\n", "During the configure step, your docker file will be generated based on your application code\n", "\n", @@ -458,23 +458,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "from boto3.session import Session\n", - "boto_session = Session()\n", - "region = boto_session.region_name\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", "\n", - "agentcore_runtime = Runtime()\n", + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", "\n", "agent_name = \"langgraph_claude_getting_started\"\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"langgraph_bedrock.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=agent_name\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"langgraph_bedrock.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "response" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -498,7 +575,24 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result = agentcore_runtime.launch()" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['langgraph_bedrock.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -518,15 +612,14 @@ "outputs": [], "source": [ "import time\n", - "status_response = agentcore_runtime.status()\n", - "status = status_response.endpoint['status']\n", "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", + "status = create_response.get('status', 'CREATING')\n", "while status not in end_status:\n", - " time.sleep(10)\n", - " status_response = agentcore_runtime.status()\n", - " status = status_response.endpoint['status']\n", - " print(status)\n", - "status" + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" ] }, { @@ -550,8 +643,30 @@ "metadata": {}, "outputs": [], "source": [ - "invoke_response = agentcore_runtime.invoke({\"prompt\": \"How much is 2+2?\"})\n", - "invoke_response" + "import boto3\n", + "import json\n", + "from IPython.display import Markdown, display\n", + "\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", + "\n", + "invoke_response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"How is the weather now in Athens ?\"})\n", + ")\n", + "\n", + "# Capture the runtime session ID for lifecycle management\n", + "runtime_session_id = invoke_response.get('runtimeSessionId')\n", + "print(f\"Runtime Session ID: {runtime_session_id}\")\n", + "\n", + "try:\n", + " events = []\n", + " for event in invoke_response.get(\"response\", []):\n", + " events.append(event)\n", + "except Exception as e:\n", + " events = [f\"Error reading EventStream: {e}\"]\n", + "response_text = json.loads(events[0].decode(\"utf-8\"))\n", + "display(Markdown(response_text))\n" ] }, { @@ -687,60 +802,68 @@ "outputs": [], "source": [ "# --- Lifecycle Configuration Demo ---\n", - "# In production, choose a timeout appropriate for your workload:\n", - "# - Development/testing: 5-15 minutes\n", - "# - Interactive sessions: 30-60 minutes\n", - "# - Long-running workloads: adjust as needed\n", - "#\n", + "# Create a second runtime with a 5-minute (300 second) idle timeout\n", + "# to show how lifecycle configuration affects session behavior.\n", "\n", - "agentcore_runtime_short = Runtime()\n", "agent_name_short = \"langgraph_claude_short_timeout\"\n", "\n", - "# Configure with shorter idle timeout\n", - "response_short = agentcore_runtime_short.configure(\n", - " entrypoint=\"langgraph_bedrock.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=agent_name_short\n", + "# Create IAM execution role for the short-timeout runtime\n", + "role_arn_short = create_or_get_execution_role(agent_name_short, region, account_id)\n", + "\n", + "# Package and upload (reuses existing S3 bucket)\n", + "bucket_name_short, s3_key_short = package_and_upload_to_s3(\n", + " agent_name_short,\n", + " [\"langgraph_bedrock.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", "\n", - "# Launch the second runtime\n", - "launch_result_short = agentcore_runtime_short.launch()\n", - "print(f\"Second runtime launched: {launch_result_short.agent_id}\")\n", + "# Create the second AgentCore Runtime\n", + "create_response_short = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name_short,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name_short, 'prefix': s3_key_short}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['langgraph_bedrock.py']\n", + " }\n", + " },\n", + " roleArn=role_arn_short,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id_short = create_response_short['agentRuntimeId']\n", + "agent_runtime_arn_short = create_response_short['agentRuntimeArn']\n", + "print(f\"Second runtime launched: {agent_runtime_id_short}\")\n", "\n", - "# Wait for it to be ready\n", - "status_response_short = agentcore_runtime_short.status()\n", - "status_short = status_response_short.endpoint['status']\n", + "# Wait for second runtime to be ready\n", + "status_short = create_response_short.get('status', 'CREATING')\n", "while status_short not in ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']:\n", - " time.sleep(10)\n", - " status_response_short = agentcore_runtime_short.status()\n", - " status_short = status_response_short.endpoint['status']\n", + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id_short)\n", + " status_short = r['status']\n", " print(f\"Short timeout runtime status: {status_short}\")\n", "\n", - "# Now update the runtime with shorter idle timeout using boto3\n", - "# UpdateAgentRuntime is a full-replacement API — we must re-supply all required fields.\n", - "# First, retrieve the current runtime configuration.\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", - "current_runtime = agentcore_control_client.get_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id\n", - ")\n", - "\n", - "update_response = agentcore_control_client.update_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id,\n", + "# Update the second runtime with a 5-minute idle timeout\n", + "# UpdateAgentRuntime is a full-replacement API — re-supply all required fields\n", + "current_runtime = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id_short)\n", + "update_response = agentcore_control.update_agent_runtime(\n", + " agentRuntimeId=agent_runtime_id_short,\n", " agentRuntimeArtifact=current_runtime['agentRuntimeArtifact'],\n", " roleArn=current_runtime['roleArn'],\n", " networkConfiguration=current_runtime['networkConfiguration'],\n", - " lifecycleConfiguration={\n", - " 'idleRuntimeSessionTimeout': 300 # 5 minutes\n", - " }\n", + " lifecycleConfiguration={'idleRuntimeSessionTimeout': 300} # 5 minutes\n", ")\n", - "print(f\"✅ Runtime updated with 5-minute idle timeout\")\n", + "print(f\"Second runtime updated with 5-minute idle timeout\")\n", "\n", "# Invoke the second runtime to verify it works\n", - "invoke_response_short = agentcore_runtime_short.invoke({\"prompt\": \"What is 3+3?\"})\n", - "print(f\"Second runtime response: {invoke_response_short['response'][0]}\")" + "invoke_short = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn_short,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"What is 3+3?\"})\n", + ")\n", + "short_events = [e for e in invoke_short.get(\"response\", [])]\n", + "if short_events:\n", + " print(f\"Second runtime response: {json.loads(short_events[0].decode('utf-8'))}\")\n" ] }, { @@ -760,7 +883,9 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result.ecr_uri, launch_result.agent_id, launch_result.ecr_uri.split('/')[1]" + "# Runtime info\n", + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -770,68 +895,50 @@ "metadata": {}, "outputs": [], "source": [ - "# --- Stop active sessions to release microVM resources ---\n", + "# --- Cleanup: Delete runtimes and S3 artifacts ---\n", "import boto3\n", "\n", "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", - "ecr_client = boto3.client('ecr', region_name=region)\n", - "\n", - "# Stop the active session to release its microVM resources\n", - "# In production, this is how you end individual user sessions while keeping the runtime alive\n", - "# AgentCore Runtime costs are based on vCPU and Memory — stopping sessions avoids undesired costs\n", - "# Note: If the session was already stopped in the earlier demo cell, this will raise a\n", - "# ResourceNotFoundException — the except block handles that gracefully.\n", + "s3_client = boto3.client('s3', region_name=region)\n", + "\n", + "# Stop the active session to release microVM resources\n", "if 'runtime_session_id' in locals() and runtime_session_id:\n", " try:\n", " agentcore_client.stop_runtime_session(\n", - " agentRuntimeArn=launch_result.agent_arn,\n", + " agentRuntimeArn=agent_runtime_arn,\n", " runtimeSessionId=runtime_session_id,\n", " qualifier='DEFAULT'\n", " )\n", - " print(f\"✅ Session '{runtime_session_id}' stopped\")\n", + " print(f\"Session '{runtime_session_id}' stopped\")\n", " except Exception as e:\n", - " print(f\"⚠️ Failed to stop session '{runtime_session_id}': {e}\")\n", + " print(f\"Could not stop session: {e}\")\n", "\n", - "# --- Delete both runtimes ---\n", - "# Original runtime\n", + "# Delete original runtime\n", "try:\n", - " agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result.agent_id,\n", - " )\n", - " print(f\"✅ Original runtime '{launch_result.agent_id}' deleted\")\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " print(f\"Runtime '{agent_name}' deleted\")\n", "except Exception as e:\n", - " print(f\"⚠️ Failed to delete original runtime: {e}\")\n", + " print(f\"Could not delete runtime: {e}\")\n", "\n", - "# Short-timeout runtime\n", - "if 'launch_result_short' in locals():\n", + "# Delete short-timeout runtime\n", + "if 'agent_runtime_id_short' in dir():\n", " try:\n", - " agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id,\n", - " )\n", - " print(f\"✅ Short-timeout runtime '{launch_result_short.agent_id}' deleted\")\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=agent_runtime_id_short)\n", + " print(f\"Short-timeout runtime deleted\")\n", " except Exception as e:\n", - " print(f\"⚠️ Failed to delete short-timeout runtime: {e}\")\n", + " print(f\"Could not delete short-timeout runtime: {e}\")\n", "\n", - "# --- Delete ECR repositories ---\n", + "# Delete S3 deployment artifacts\n", "try:\n", - " ecr_client.delete_repository(\n", - " repositoryName=launch_result.ecr_uri.split('/')[1],\n", - " force=True\n", - " )\n", - " print(f\"✅ ECR repository '{launch_result.ecr_uri.split('/')[1]}' deleted\")\n", + " s3_client.delete_object(Bucket=bucket_name, Key=s3_key)\n", + " print(f\"S3 artifact deleted: s3://{bucket_name}/{s3_key}\")\n", "except Exception as e:\n", - " print(f\"⚠️ Failed to delete ECR repository: {e}\")\n", + " print(f\"Could not delete S3 artifact: {e}\")\n", "\n", - "if 'launch_result_short' in locals():\n", - " try:\n", - " ecr_client.delete_repository(\n", - " repositoryName=launch_result_short.ecr_uri.split('/')[1],\n", - " force=True\n", - " )\n", - " print(f\"✅ Second ECR repository '{launch_result_short.ecr_uri.split('/')[1]}' deleted\")\n", - " except Exception as e:\n", - " print(f\"⚠️ Failed to delete second ECR repository: {e}\")" + "# Delete local config file if it exists\n", + "import subprocess\n", + "subprocess.run([\"rm\", \"-f\", \".bedrock_agentcore.yaml\"], capture_output=True)\n", + "print(\"Cleanup complete.\")\n" ] }, { @@ -864,4 +971,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/03-strands-with-openai-model/README.md b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/03-strands-with-openai-model/README.md index 93c6e2037..7af401f79 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/03-strands-with-openai-model/README.md +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/03-strands-with-openai-model/README.md @@ -2,9 +2,9 @@ ## Overview -In this tutorial we will learn how to host your existing agent, using Amazon Bedrock AgentCore Runtime. +In this tutorial we will learn how to host your existing agent, using Amazon Bedrock AgentCore Runtime. -We will focus on a Strands Agents with OpenAI model example. For Strands Agents with Amazon Bedrock model check [here](../01-strands-with-bedrock-model) and +We will focus on a Strands Agents with OpenAI model example. For Strands Agents with Amazon Bedrock model check [here](../01-strands-with-bedrock-model) and for LangGraph with Amazon Bedrock model check [here](../02-langgraph-with-bedrock-model) @@ -21,20 +21,119 @@ for LangGraph with Amazon Bedrock model check [here](../02-langgraph-with-bedroc | Example complexity | Easy | | SDK used | Amazon BedrockAgentCore Python SDK and boto3 | -### Tutorial Architecture +--- -In this tutorial we will describe how to deploy an existing agent to AgentCore runtime. +## Architecture -For demonstration purposes, we will use a Strands Agent using Amazon Bedrock models +When hosting agents, the SDK automatically: -In our example we will use a very simple agent with two tools: `get_weather` and `get_time`. +- Hosts your agent on port `8080` +- Provides two key endpoints: + - **`/invocations`**: Primary agent interaction (JSON input → JSON/SSE output) + - **`/ping`**: Health check for monitoring
+AgentCore Runtime is **model-agnostic** — it can host agents using any LLM provider (Amazon Bedrock, OpenAI, Azure OpenAI, Gemini, etc.) via Strands Agents' LiteLLM integration. The lifecycle: + +1. **Local experimentation** — run your agent locally with OpenAI credentials in env vars +2. **Wrap with SDK** — add `BedrockAgentCoreApp` decorator +3. **Deploy to Runtime** — package code, upload to S3, create runtime via boto3 with env var injection +4. **Invoke** — call via boto3 `invoke_agent_runtime` + +--- + +## What's This Feature + +Amazon Bedrock AgentCore Runtime is framework and model-agnostic. This tutorial demonstrates hosting a Strands agent backed by Azure OpenAI (GPT 4.1 mini), showing that you can bring your own LLM provider. + ### Tutorial key Features * Hosting Agents on Amazon Bedrock AgentCore Runtime -* Using OpenAI models -* Using Strands Agents \ No newline at end of file +* Using OpenAI models (Azure OpenAI GPT 4.1 mini via LiteLLM) +* Using Strands Agents +* CodeZip deployment (no Docker required) via boto3 `create_agent_runtime` +* Session lifecycle management with `stop_runtime_session` +* Environment variable injection for API credentials + +--- + +## CLI Commands + +> **CLI version**: `agentcore@0.11.0` +> +> Install or update: `npm install -g @aws/agentcore@0.11.0` + +### 1. Create a new project + +```bash +agentcore create \ + --name strandsopenai \ + --framework Strands \ + --model-provider OpenAI \ + --api-key "" \ + --build CodeZip \ + --skip-git \ + --skip-install \ + --json +``` + +### 2. Replace the generated agent code + +```bash +cp strands_agents_openai.py app/strandsopenai/main.py +cp requirements.txt app/strandsopenai/requirements.txt +``` + +### 3. Deploy to AgentCore Runtime + +```bash +cd strandsopenai +agentcore deploy -y --json +``` + +### 4. Check deployment status + +```bash +agentcore status --json +``` + +### 5. Invoke the deployed agent + +```bash +agentcore invoke "What is the weather now?" --json +``` + +### 6. View logs + +```bash +agentcore logs --since 30m -n 50 +``` + +--- + +## Cleanup + +**Using boto3** (from the notebook cleanup cell): + +```python +import boto3 +agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region) +agentcore_control.delete_agent_runtime(agentRuntimeId=agent_runtime_id) +``` + +**Using CLI** (if deployed via `agentcore deploy`): + +```bash +agentcore remove agent --name strandsopenai --json +agentcore deploy -y --json +``` + +Also delete the S3 deployment artifact: + +```python +s3 = boto3.client('s3', region_name=region) +s3.delete_object(Bucket=bucket_name, Key=s3_key) +``` diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/03-strands-with-openai-model/requirements.txt b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/03-strands-with-openai-model/requirements.txt index 606cc8967..406376977 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/03-strands-with-openai-model/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/03-strands-with-openai-model/requirements.txt @@ -6,5 +6,4 @@ starlette uvicorn aws-opentelemetry-distro-genai-beta setuptools -bedrock-agentcore<=0.1.5 -bedrock-agentcore-starter-toolkit==0.1.14 \ No newline at end of file +bedrock-agentcore diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/03-strands-with-openai-model/runtime_with_strands_and_openai_models.ipynb b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/03-strands-with-openai-model/runtime_with_strands_and_openai_models.ipynb index 40158888e..3dfa9eef8 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/03-strands-with-openai-model/runtime_with_strands_and_openai_models.ipynb +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/03-strands-with-openai-model/runtime_with_strands_and_openai_models.ipynb @@ -278,7 +278,7 @@ "source": [ "### Configure AgentCore Runtime deployment\n", "\n", - "Next we will use our starter toolkit to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the starter kit to auto create the Amazon ECR repository on launch.\n", + "Next we will use the AgentCore SDK and boto3 to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the SDK to auto create the Amazon ECR repository on launch.\n", "\n", "During the configure step, your docker file will be generated based on your application code\n", "\n", @@ -294,25 +294,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "from boto3.session import Session\n", "import boto3\n", "import json\n", - "boto_session = Session()\n", - "region = boto_session.region_name\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", "\n", - "agentcore_runtime = Runtime()\n", + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", "\n", "agent_name = \"strands_openai_getting_started\"\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"strands_agents_openai.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=agent_name\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"strands_agents_openai.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "response" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -336,7 +411,24 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result = agentcore_runtime.launch()" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['strands_agents_openai.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -356,15 +448,14 @@ "outputs": [], "source": [ "import time\n", - "status_response = agentcore_runtime.status()\n", - "status = status_response.endpoint['status']\n", "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", + "status = create_response.get('status', 'CREATING')\n", "while status not in end_status:\n", - " time.sleep(10)\n", - " status_response = agentcore_runtime.status()\n", - " status = status_response.endpoint['status']\n", - " print(status)\n", - "status" + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" ] }, { @@ -388,8 +479,30 @@ "metadata": {}, "outputs": [], "source": [ - "invoke_response = agentcore_runtime.invoke({\"prompt\": \"Hi, what can you do?\"})\n", - "invoke_response" + "import boto3\n", + "import json\n", + "from IPython.display import Markdown, display\n", + "\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", + "\n", + "invoke_response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"How is the weather now in Athens ?\"})\n", + ")\n", + "\n", + "# Capture the runtime session ID for lifecycle management\n", + "runtime_session_id = invoke_response.get('runtimeSessionId')\n", + "print(f\"Runtime Session ID: {runtime_session_id}\")\n", + "\n", + "try:\n", + " events = []\n", + " for event in invoke_response.get(\"response\", []):\n", + " events.append(event)\n", + "except Exception as e:\n", + " events = [f\"Error reading EventStream: {e}\"]\n", + "response_text = json.loads(events[0].decode(\"utf-8\"))\n", + "display(Markdown(response_text))\n" ] }, { @@ -521,60 +634,68 @@ "outputs": [], "source": [ "# --- Lifecycle Configuration Demo ---\n", - "# In production, choose a timeout appropriate for your workload:\n", - "# - Development/testing: 5-15 minutes\n", - "# - Interactive sessions: 30-60 minutes\n", - "# - Long-running workloads: adjust as needed\n", - "#\n", + "# Create a second runtime with a 5-minute (300 second) idle timeout\n", + "# to show how lifecycle configuration affects session behavior.\n", "\n", - "agentcore_runtime_short = Runtime()\n", "agent_name_short = \"strands_openai_short_timeout\"\n", "\n", - "# Configure with shorter idle timeout\n", - "response_short = agentcore_runtime_short.configure(\n", - " entrypoint=\"strands_agents_openai.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=agent_name_short\n", + "# Create IAM execution role for the short-timeout runtime\n", + "role_arn_short = create_or_get_execution_role(agent_name_short, region, account_id)\n", + "\n", + "# Package and upload (reuses existing S3 bucket)\n", + "bucket_name_short, s3_key_short = package_and_upload_to_s3(\n", + " agent_name_short,\n", + " [\"strands_agents_openai.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", "\n", - "# Launch the second runtime\n", - "launch_result_short = agentcore_runtime_short.launch()\n", - "print(f\"Second runtime launched: {launch_result_short.agent_id}\")\n", + "# Create the second AgentCore Runtime\n", + "create_response_short = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name_short,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name_short, 'prefix': s3_key_short}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['strands_agents_openai.py']\n", + " }\n", + " },\n", + " roleArn=role_arn_short,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id_short = create_response_short['agentRuntimeId']\n", + "agent_runtime_arn_short = create_response_short['agentRuntimeArn']\n", + "print(f\"Second runtime launched: {agent_runtime_id_short}\")\n", "\n", - "# Wait for it to be ready\n", - "status_response_short = agentcore_runtime_short.status()\n", - "status_short = status_response_short.endpoint['status']\n", + "# Wait for second runtime to be ready\n", + "status_short = create_response_short.get('status', 'CREATING')\n", "while status_short not in ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']:\n", - " time.sleep(10)\n", - " status_response_short = agentcore_runtime_short.status()\n", - " status_short = status_response_short.endpoint['status']\n", + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id_short)\n", + " status_short = r['status']\n", " print(f\"Short timeout runtime status: {status_short}\")\n", "\n", - "# Now update the runtime with shorter idle timeout using boto3\n", - "# UpdateAgentRuntime is a full-replacement API — we must re-supply all required fields.\n", - "# First, retrieve the current runtime configuration.\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", - "current_runtime = agentcore_control_client.get_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id\n", - ")\n", - "\n", - "update_response = agentcore_control_client.update_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id,\n", + "# Update the second runtime with a 5-minute idle timeout\n", + "# UpdateAgentRuntime is a full-replacement API — re-supply all required fields\n", + "current_runtime = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id_short)\n", + "update_response = agentcore_control.update_agent_runtime(\n", + " agentRuntimeId=agent_runtime_id_short,\n", " agentRuntimeArtifact=current_runtime['agentRuntimeArtifact'],\n", " roleArn=current_runtime['roleArn'],\n", " networkConfiguration=current_runtime['networkConfiguration'],\n", - " lifecycleConfiguration={\n", - " 'idleRuntimeSessionTimeout': 300 # 5 minutes\n", - " }\n", + " lifecycleConfiguration={'idleRuntimeSessionTimeout': 300} # 5 minutes\n", ")\n", - "print(f\"✅ Runtime updated with 5-minute idle timeout\")\n", + "print(f\"Second runtime updated with 5-minute idle timeout\")\n", "\n", "# Invoke the second runtime to verify it works\n", - "invoke_response_short = agentcore_runtime_short.invoke({\"prompt\": \"What is 3+3?\"})\n", - "print(f\"Second runtime response: {invoke_response_short['response'][0]}\")" + "invoke_short = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn_short,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"What is 3+3?\"})\n", + ")\n", + "short_events = [e for e in invoke_short.get(\"response\", [])]\n", + "if short_events:\n", + " print(f\"Second runtime response: {json.loads(short_events[0].decode('utf-8'))}\")\n" ] }, { @@ -594,68 +715,50 @@ "metadata": {}, "outputs": [], "source": [ - "# --- Stop active sessions to release microVM resources ---\n", + "# --- Cleanup: Delete runtimes and S3 artifacts ---\n", "import boto3\n", "\n", "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", - "ecr_client = boto3.client('ecr', region_name=region)\n", - "\n", - "# Stop the active session to release its microVM resources\n", - "# In production, this is how you end individual user sessions while keeping the runtime alive\n", - "# AgentCore Runtime costs are based on vCPU and Memory — stopping sessions avoids undesired costs\n", - "# Note: If the session was already stopped in the earlier demo cell, this will raise a\n", - "# ResourceNotFoundException — the except block handles that gracefully.\n", + "s3_client = boto3.client('s3', region_name=region)\n", + "\n", + "# Stop the active session to release microVM resources\n", "if 'runtime_session_id' in locals() and runtime_session_id:\n", " try:\n", " agentcore_client.stop_runtime_session(\n", - " agentRuntimeArn=launch_result.agent_arn,\n", + " agentRuntimeArn=agent_runtime_arn,\n", " runtimeSessionId=runtime_session_id,\n", " qualifier='DEFAULT'\n", " )\n", - " print(f\"✅ Session '{runtime_session_id}' stopped\")\n", + " print(f\"Session '{runtime_session_id}' stopped\")\n", " except Exception as e:\n", - " print(f\"⚠️ Failed to stop session '{runtime_session_id}': {e}\")\n", + " print(f\"Could not stop session: {e}\")\n", "\n", - "# --- Delete both runtimes ---\n", - "# Original runtime\n", + "# Delete original runtime\n", "try:\n", - " agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result.agent_id,\n", - " )\n", - " print(f\"✅ Original runtime '{launch_result.agent_id}' deleted\")\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " print(f\"Runtime '{agent_name}' deleted\")\n", "except Exception as e:\n", - " print(f\"⚠️ Failed to delete original runtime: {e}\")\n", + " print(f\"Could not delete runtime: {e}\")\n", "\n", - "# Short-timeout runtime\n", - "if 'launch_result_short' in locals():\n", + "# Delete short-timeout runtime\n", + "if 'agent_runtime_id_short' in dir():\n", " try:\n", - " agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id,\n", - " )\n", - " print(f\"✅ Short-timeout runtime '{launch_result_short.agent_id}' deleted\")\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=agent_runtime_id_short)\n", + " print(f\"Short-timeout runtime deleted\")\n", " except Exception as e:\n", - " print(f\"⚠️ Failed to delete short-timeout runtime: {e}\")\n", + " print(f\"Could not delete short-timeout runtime: {e}\")\n", "\n", - "# --- Delete ECR repositories ---\n", + "# Delete S3 deployment artifacts\n", "try:\n", - " ecr_client.delete_repository(\n", - " repositoryName=launch_result.ecr_uri.split('/')[1],\n", - " force=True\n", - " )\n", - " print(f\"✅ ECR repository '{launch_result.ecr_uri.split('/')[1]}' deleted\")\n", + " s3_client.delete_object(Bucket=bucket_name, Key=s3_key)\n", + " print(f\"S3 artifact deleted: s3://{bucket_name}/{s3_key}\")\n", "except Exception as e:\n", - " print(f\"⚠️ Failed to delete ECR repository: {e}\")\n", + " print(f\"Could not delete S3 artifact: {e}\")\n", "\n", - "if 'launch_result_short' in locals():\n", - " try:\n", - " ecr_client.delete_repository(\n", - " repositoryName=launch_result_short.ecr_uri.split('/')[1],\n", - " force=True\n", - " )\n", - " print(f\"✅ Second ECR repository '{launch_result_short.ecr_uri.split('/')[1]}' deleted\")\n", - " except Exception as e:\n", - " print(f\"⚠️ Failed to delete second ECR repository: {e}\")" + "# Delete local config file if it exists\n", + "import subprocess\n", + "subprocess.run([\"rm\", \"-f\", \".bedrock_agentcore.yaml\"], capture_output=True)\n", + "print(\"Cleanup complete.\")\n" ] } ], @@ -680,4 +783,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/04-crewai-with-bedrock-model/research_crew/requirements.txt b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/04-crewai-with-bedrock-model/research_crew/requirements.txt index 413b34e2e..7c80557dc 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/04-crewai-with-bedrock-model/research_crew/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/04-crewai-with-bedrock-model/research_crew/requirements.txt @@ -1,5 +1,5 @@ crewai crewai-tools -bedrock-agentcore<=0.1.5 +bedrock-agentcore langchain-community duckduckgo-search \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/04-crewai-with-bedrock-model/runtime-with-crewai-and-bedrock-models.ipynb b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/04-crewai-with-bedrock-model/runtime-with-crewai-and-bedrock-models.ipynb index 0e7be44e9..512d99293 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/04-crewai-with-bedrock-model/runtime-with-crewai-and-bedrock-models.ipynb +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/04-crewai-with-bedrock-model/runtime-with-crewai-and-bedrock-models.ipynb @@ -436,7 +436,7 @@ "source": [ "#### Configure AgentCore Runtime deployment\n", "\n", - "First we will use our starter toolkit to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the starter kit to auto create the Amazon ECR repository on launch.\n", + "First we will use our AgentCore SDK to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the AgentCore SDK to auto create the Amazon ECR repository on launch.\n", "\n", "AgentCore configure is required to generate a Dockerfile holding a blueprint for the Docker container the workload will be running in and a .bedrock_agentcore.yaml holding the agentic workload's configuration. During the configure step, your docker file will be generated based on your application code.\n", "\n", @@ -452,21 +452,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "from boto3.session import Session\n", - "boto_session = Session()\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", + "\n", + "boto_session = boto3.session.Session()\n", "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", "\n", - "agentcore_runtime = Runtime()\n", "agent_name = \"research_crew_getting_started\"\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"research_crew/research_crew.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " region=region,\n", - " agent_name=agent_name\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"research_crew/research_crew.py\", \"research_crew/requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "response" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -491,7 +570,24 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result = agentcore_runtime.launch()" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['research_crew/research_crew.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -512,15 +608,14 @@ "outputs": [], "source": [ "import time\n", - "status_response = agentcore_runtime.status()\n", - "status = status_response.endpoint['status']\n", "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", + "status = create_response.get('status', 'CREATING')\n", "while status not in end_status:\n", - " time.sleep(10)\n", - " status_response = agentcore_runtime.status()\n", - " status = status_response.endpoint['status']\n", - " print(status)\n", - "status" + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" ] }, { @@ -657,60 +752,68 @@ "outputs": [], "source": [ "# --- Lifecycle Configuration Demo ---\n", - "# In production, choose a timeout appropriate for your workload:\n", - "# - Development/testing: 5-15 minutes\n", - "# - Interactive sessions: 30-60 minutes\n", - "# - Long-running workloads: adjust as needed\n", - "#\n", + "# Create a second runtime with a 5-minute (300 second) idle timeout\n", + "# to show how lifecycle configuration affects session behavior.\n", "\n", - "agentcore_runtime_short = Runtime()\n", "agent_name_short = \"crewai_claude_short_timeout\"\n", "\n", - "# Configure with shorter idle timeout\n", - "response_short = agentcore_runtime_short.configure(\n", - " entrypoint=\"research_crew/research_crew.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"research_crew/requirements.txt\",\n", - " region=region,\n", - " agent_name=agent_name_short\n", + "# Create IAM execution role for the short-timeout runtime\n", + "role_arn_short = create_or_get_execution_role(agent_name_short, region, account_id)\n", + "\n", + "# Package and upload (reuses existing S3 bucket)\n", + "bucket_name_short, s3_key_short = package_and_upload_to_s3(\n", + " agent_name_short,\n", + " [\"research_crew/research_crew.py\", \"research_crew/requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", "\n", - "# Launch the second runtime\n", - "launch_result_short = agentcore_runtime_short.launch()\n", - "print(f\"Second runtime launched: {launch_result_short.agent_id}\")\n", + "# Create the second AgentCore Runtime\n", + "create_response_short = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name_short,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name_short, 'prefix': s3_key_short}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['research_crew/research_crew.py']\n", + " }\n", + " },\n", + " roleArn=role_arn_short,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id_short = create_response_short['agentRuntimeId']\n", + "agent_runtime_arn_short = create_response_short['agentRuntimeArn']\n", + "print(f\"Second runtime launched: {agent_runtime_id_short}\")\n", "\n", - "# Wait for it to be ready\n", - "status_response_short = agentcore_runtime_short.status()\n", - "status_short = status_response_short.endpoint['status']\n", + "# Wait for second runtime to be ready\n", + "status_short = create_response_short.get('status', 'CREATING')\n", "while status_short not in ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']:\n", - " time.sleep(10)\n", - " status_response_short = agentcore_runtime_short.status()\n", - " status_short = status_response_short.endpoint['status']\n", + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id_short)\n", + " status_short = r['status']\n", " print(f\"Short timeout runtime status: {status_short}\")\n", "\n", - "# Now update the runtime with shorter idle timeout using boto3\n", - "# UpdateAgentRuntime is a full-replacement API — we must re-supply all required fields.\n", - "# First, retrieve the current runtime configuration.\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", - "current_runtime = agentcore_control_client.get_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id\n", - ")\n", - "\n", - "update_response = agentcore_control_client.update_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id,\n", + "# Update the second runtime with a 5-minute idle timeout\n", + "# UpdateAgentRuntime is a full-replacement API — re-supply all required fields\n", + "current_runtime = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id_short)\n", + "update_response = agentcore_control.update_agent_runtime(\n", + " agentRuntimeId=agent_runtime_id_short,\n", " agentRuntimeArtifact=current_runtime['agentRuntimeArtifact'],\n", " roleArn=current_runtime['roleArn'],\n", " networkConfiguration=current_runtime['networkConfiguration'],\n", - " lifecycleConfiguration={\n", - " 'idleRuntimeSessionTimeout': 300 # 5 minutes\n", - " }\n", + " lifecycleConfiguration={'idleRuntimeSessionTimeout': 300} # 5 minutes\n", ")\n", - "print(f\"✅ Runtime updated with 5-minute idle timeout\")\n", + "print(f\"Second runtime updated with 5-minute idle timeout\")\n", "\n", "# Invoke the second runtime to verify it works\n", - "invoke_response_short = agentcore_runtime_short.invoke({\"prompt\": \"What is 3+3?\"})\n", - "print(f\"Second runtime response: {invoke_response_short['response'][0]}\")" + "invoke_short = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn_short,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"What is 3+3?\"})\n", + ")\n", + "short_events = [e for e in invoke_short.get(\"response\", [])]\n", + "if short_events:\n", + " print(f\"Second runtime response: {json.loads(short_events[0].decode('utf-8'))}\")\n" ] }, { @@ -730,7 +833,8 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result.ecr_uri, launch_result.agent_id, launch_result.ecr_uri.split('/')[1]" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -740,68 +844,8 @@ "metadata": {}, "outputs": [], "source": [ - "# --- Stop active sessions to release microVM resources ---\n", - "import boto3\n", - "\n", - "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", - "ecr_client = boto3.client('ecr', region_name=region)\n", - "\n", - "# Stop the active session to release its microVM resources\n", - "# In production, this is how you end individual user sessions while keeping the runtime alive\n", - "# AgentCore Runtime costs are based on vCPU and Memory — stopping sessions avoids undesired costs\n", - "# Note: If the session was already stopped in the earlier demo cell, this will raise a\n", - "# ResourceNotFoundException — the except block handles that gracefully.\n", - "if 'runtime_session_id' in locals() and runtime_session_id:\n", - " try:\n", - " agentcore_client.stop_runtime_session(\n", - " agentRuntimeArn=launch_result.agent_arn,\n", - " runtimeSessionId=runtime_session_id,\n", - " qualifier='DEFAULT'\n", - " )\n", - " print(f\"✅ Session '{runtime_session_id}' stopped\")\n", - " except Exception as e:\n", - " print(f\"⚠️ Failed to stop session '{runtime_session_id}': {e}\")\n", - "\n", - "# --- Delete both runtimes ---\n", - "# Original runtime\n", - "try:\n", - " agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result.agent_id,\n", - " )\n", - " print(f\"✅ Original runtime '{launch_result.agent_id}' deleted\")\n", - "except Exception as e:\n", - " print(f\"⚠️ Failed to delete original runtime: {e}\")\n", - "\n", - "# Short-timeout runtime\n", - "if 'launch_result_short' in locals():\n", - " try:\n", - " agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id,\n", - " )\n", - " print(f\"✅ Short-timeout runtime '{launch_result_short.agent_id}' deleted\")\n", - " except Exception as e:\n", - " print(f\"⚠️ Failed to delete short-timeout runtime: {e}\")\n", - "\n", - "# --- Delete ECR repositories ---\n", - "try:\n", - " ecr_client.delete_repository(\n", - " repositoryName=launch_result.ecr_uri.split('/')[1],\n", - " force=True\n", - " )\n", - " print(f\"✅ ECR repository '{launch_result.ecr_uri.split('/')[1]}' deleted\")\n", - "except Exception as e:\n", - " print(f\"⚠️ Failed to delete ECR repository: {e}\")\n", - "\n", - "if 'launch_result_short' in locals():\n", - " try:\n", - " ecr_client.delete_repository(\n", - " repositoryName=launch_result_short.ecr_uri.split('/')[1],\n", - " force=True\n", - " )\n", - " print(f\"✅ Second ECR repository '{launch_result_short.ecr_uri.split('/')[1]}' deleted\")\n", - " except Exception as e:\n", - " print(f\"⚠️ Failed to delete second ECR repository: {e}\")" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -834,4 +878,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/06-strands-with-skills/README.md b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/06-strands-with-skills/README.md index 40ade8e52..01d02a635 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/06-strands-with-skills/README.md +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/06-strands-with-skills/README.md @@ -23,27 +23,130 @@ For a basic Strands agent without skills check [here](../01-strands-with-bedrock | Example complexity | Intermediate | | SDK used | Amazon BedrockAgentCore Python SDK and boto3 | -### Tutorial Architecture +--- -In this tutorial we will describe how to deploy a skills-based agent to AgentCore Runtime. - -For demonstration purposes, we will use a Strands Agent with the `AgentSkills` plugin, which loads skill definitions from a `skills/` directory. Each skill is a folder containing a `SKILL.md` file with YAML frontmatter (`name`, `description`, `allowed-tools`) and markdown instructions. +## Architecture
-The agent uses two skills: -- **weather-reporter**: Paired with a custom `@tool` weather function, formats weather information with emoji and recommendations. -- **math-tutor**: Paired with the `calculator` tool from `strands-agents-tools`, solves math problems step-by-step. +The agent uses the `AgentSkills` plugin which scans a `skills/` directory. Each skill is a folder containing a `SKILL.md` file with YAML frontmatter (`name`, `description`, `allowed-tools`) and markdown instructions. The agent selects the appropriate skill based on the user's request. + +Two example skills are provided: +- **weather-reporter**: Defined as a file-based `SKILL.md`. Paired with a custom `@tool` weather function. +- **math-tutor**: Defined programmatically using the `Skill` class (no file needed). Paired with the `calculator` tool. + +The deployment packages the entire project directory (agent code + `skills/` directory) as a ZIP, uploads to S3, and creates the runtime using `codeConfiguration`. + +--- -The agent runs on Anthropic Claude Haiku 4.5 via Amazon Bedrock. +## What's This Feature + +The `AgentSkills` plugin enables **on-demand skill activation** — the agent loads only the skill relevant to the user's current request, keeping the context window efficient. Skills can be defined as: + +1. **File-based**: a `skills//SKILL.md` file with YAML frontmatter +2. **Programmatic**: a `Skill(name, description, instructions)` object inline in Python ### Tutorial Key Features * Hosting Agents on Amazon Bedrock AgentCore Runtime * Using the Strands `AgentSkills` plugin for on-demand specialized instructions * Defining skills with `SKILL.md` files using YAML frontmatter +* Defining skills programmatically with the `Skill` class (no file needed) +* Mixing file-based and programmatic skills in a single agent * Pairing skills with custom `@tool` functions and existing tools * Local agent experimentation before deployment -* Deploying a skills-based agent to AgentCore Runtime +* CodeZip deployment (includes `skills/` directory) via boto3 `create_agent_runtime` + +--- + +## CLI Commands + +> **CLI version**: `agentcore@0.11.0` +> +> Install or update: `npm install -g @aws/agentcore@0.11.0` + +### 1. Create a new project + +```bash +agentcore create \ + --name strandsskills \ + --framework Strands \ + --model-provider Bedrock \ + --build CodeZip \ + --skip-git \ + --skip-install \ + --json +``` + +### 2. Copy agent code and skills directory + +```bash +cp agent_with_skills.py app/strandsskills/main.py +cp requirements.txt app/strandsskills/requirements.txt +cp -r skills/ app/strandsskills/skills/ +``` + +### 3. Deploy to AgentCore Runtime + +```bash +cd strandsskills +agentcore deploy -y --json +``` + +### 4. Check deployment status + +```bash +agentcore status --json +``` + +### 5. Invoke the deployed agent — weather skill + +```bash +agentcore invoke "What's the weather like today? Give me a full report with emoji and activity recommendations." --json +``` + +### 6. Invoke the deployed agent — math skill + +```bash +agentcore invoke "Help me solve 23+458*89" --json +``` + +### 7. View logs + +```bash +agentcore logs --since 30m -n 50 +``` + +--- + +## Cleanup + +**Using boto3** (from the notebook cleanup cell): + +```python +import boto3 +agentcore_control = boto3.client('bedrock-agentcore-control', region_name=REGION) +runtimes = agentcore_control.list_agent_runtimes() +agent_id = next( + (r["agentRuntimeId"] for r in runtimes["agentRuntimes"] if r["agentRuntimeName"] == agent_name), + None +) +if agent_id: + agentcore_control.delete_agent_runtime(agentRuntimeId=agent_id) +``` + +**Using CLI** (if deployed via `agentcore deploy`): + +```bash +agentcore remove agent --name strandsskills --json +agentcore deploy -y --json +``` + +Also delete the S3 deployment artifact: + +```python +s3 = boto3.client('s3', region_name=REGION) +s3.delete_object(Bucket=bucket_name, Key=s3_key) +``` diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/06-strands-with-skills/requirements.txt b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/06-strands-with-skills/requirements.txt index 3a3f3ffd7..90ebb49ad 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/06-strands-with-skills/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/06-strands-with-skills/requirements.txt @@ -2,5 +2,4 @@ strands-agents strands-agents-tools uv boto3 -bedrock-agentcore<=0.1.5 -bedrock-agentcore-starter-toolkit==0.1.14 +bedrock-agentcore diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/06-strands-with-skills/runtime_with_strands_and_skills.ipynb b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/06-strands-with-skills/runtime_with_strands_and_skills.ipynb index 240568f51..8c4bf3ff7 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/06-strands-with-skills/runtime_with_strands_and_skills.ipynb +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/06-strands-with-skills/runtime_with_strands_and_skills.ipynb @@ -1,513 +1,627 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "a1b2c3d4e5f6a7b8", - "metadata": {}, - "source": [ - "# Strands Agent with AgentSkills Plugin on Amazon Bedrock AgentCore Runtime\n", - "\n", - "## Overview\n", - "\n", - "This tutorial demonstrates how to build a Strands agent using the `AgentSkills` plugin for on-demand specialized instructions, run it locally, and deploy it to Amazon Bedrock AgentCore Runtime.\n", - "\n", - "The `AgentSkills` plugin supports two ways to define skills: **file-based** (a `SKILL.md` file with YAML frontmatter) and **programmatic** (a `Skill` object defined inline in Python). Both approaches can be mixed in the same agent.\n", - "\n", - "We will walk through two example skills — a **weather-reporter** skill defined as a file-based `SKILL.md`, and a **math-tutor** skill defined programmatically using the `Skill` class. We will first experiment locally, then deploy the agent to AgentCore Runtime.\n", - "\n", - "### Tutorial Details\n", - "\n", - "| Field | Value |\n", - "|---|---|\n", - "| Tutorial type | Jupyter Notebook |\n", - "| Agent type | Strands Agent with AgentSkills Plugin |\n", - "| Agentic Framework | Strands Agents |\n", - "| LLM model | Amazon Bedrock (Claude Haiku 4.5) |\n", - "| Tutorial components | Skills (SKILL.md), AgentSkills Plugin, Local Experimentation, AgentCore Runtime Deployment |\n", - "| Tutorial vertical | General / Developer Education |\n", - "| Example complexity | Intermediate |\n", - "| SDK used | bedrock-agentcore-starter-toolkit, strands-agents |\n", - "\n", - "### Tutorial Architecture\n", - "\n", - "The agent uses the `AgentSkills` plugin which scans a `skills/` directory. Each skill is a `SKILL.md` file with YAML frontmatter (`name`, `description`, `allowed-tools`) and markdown instructions. The agent selects the appropriate skill based on the user's request.\n", - "\n", - "Two example skills are provided:\n", - "- **weather-reporter**: Defined as a file-based `SKILL.md`. Paired with a custom `@tool` weather function, formats weather information with emoji, temperature ranges, and activity recommendations.\n", - "- **math-tutor**: Defined programmatically using the `Skill` class (no file needed). Paired with the `calculator` tool from `strands-agents-tools`, solves math problems step-by-step showing all work.\n", - "\n", - "### Tutorial Key Features\n", - "\n", - "* Two skill definition approaches: file-based (`SKILL.md`) and programmatic (`Skill` class)\n", - "* Mixing file-based and programmatic skills in a single `AgentSkills` plugin\n", - "* On-demand skill activation based on user intent\n", - "* Local experimentation before cloud deployment\n", - "* Seamless deployment to Amazon Bedrock AgentCore Runtime\n" - ] - }, - { - "cell_type": "markdown", - "id": "b2c3d4e5f6a7b8c9", - "metadata": {}, - "source": [ - "## Prerequisites\n", - "\n", - "To execute this tutorial you will need:\n", - "* Python 3.10+\n", - "* AWS credentials configured (`aws configure`)\n", - "* Amazon Bedrock AgentCore SDK (`bedrock-agentcore`, `bedrock-agentcore-starter-toolkit`)\n", - "* Strands Agents (`strands-agents`, `strands-agents-tools`)\n", - "* Access to Amazon Bedrock models (Claude Haiku 4.5)\n", - "* Docker or Finch (for AgentCore Runtime deployment)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c3d4e5f6a7b8c9d0", - "metadata": {}, - "outputs": [], - "source": [ - "%pip install -r requirements.txt" - ] - }, - { - "cell_type": "markdown", - "id": "d4e5f6a7b8c9d0e1", - "metadata": {}, - "source": [ - "## Skill Creation Approaches\n", - "\n", - "The `AgentSkills` plugin supports two ways to define skills. This tutorial demonstrates both.\n", - "\n", - "### Approach 1: File-based skill (SKILL.md)\n", - "\n", - "A file-based skill is a directory containing a `SKILL.md` file with YAML frontmatter and a markdown instruction body. This approach is ideal for skills you want to version-control, share across projects, or edit without touching Python code.\n", - "\n", - "```\n", - "skills/\n", - "└── weather-reporter/\n", - " └── SKILL.md\n", - "```\n", - "\n", - "**SKILL.md format:**\n", - "```markdown\n", - "---\n", - "name: skill-name\n", - "description: Short description used by the agent to decide when to activate this skill\n", - "allowed-tools:\n", - " - tool_name\n", - "---\n", - "\n", - "# Skill Instructions\n", - "\n", - "Markdown body with behavioral instructions for the agent.\n", - "```\n", - "\n", - "The YAML frontmatter fields:\n", - "- `name`: Unique identifier for the skill (kebab-case)\n", - "- `description`: Human-readable description used by the agent to decide when to activate the skill\n", - "- `allowed-tools`: List of tool names the skill is permitted to use\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e5f6a7b8c9d0e1f2", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "os.makedirs(\"skills/weather-reporter\", exist_ok=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f6a7b8c9d0e1f2a3", - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile skills/weather-reporter/SKILL.md\n", - "---\n", - "name: weather-reporter\n", - "description: Format weather information with emoji, temperature ranges, and activity recommendations\n", - "allowed-tools:\n", - " - weather\n", - "---\n", - "\n", - "# Weather Reporter Instructions\n", - "\n", - "You are a friendly weather reporter. When presenting weather information:\n", - "\n", - "1. **Use weather emoji** to make the report visually engaging:\n", - " - ☀️ for sunny/clear conditions\n", - " - 🌧️ for rain\n", - " - ⛅ for partly cloudy\n", - " - 🌩️ for thunderstorms\n", - " - ❄️ for snow\n", - " - 🌫️ for fog/mist\n", - "\n", - "2. **Include temperature ranges** in both Fahrenheit and Celsius\n", - "\n", - "3. **Provide activity recommendations** based on the conditions:\n", - " - Suggest outdoor activities for good weather\n", - " - Recommend indoor alternatives for bad weather\n", - " - Include clothing suggestions (e.g., \"bring an umbrella\", \"wear a light jacket\")\n", - "\n", - "4. **Format your response** as a friendly weather report with clear sections for current conditions and recommendations.\n" - ] - }, - { - "cell_type": "markdown", - "id": "a7b8c9d0e1f2a3b4", - "metadata": {}, - "source": [ - "### Approach 2: Programmatic skill (Skill class)\n", - "\n", - "A programmatic skill is defined inline in Python using the `Skill` class — no directory or file needed. This is convenient for simple skills, dynamic skill generation, or when you want to keep everything in one script.\n", - "\n", - "```python\n", - "from strands import Skill\n", - "\n", - "math_tutor = Skill(\n", - " name=\"math-tutor\",\n", - " description=\"Solve math problems step-by-step, showing all work\",\n", - " instructions=\"Break down math problems into clear steps. Show all intermediate calculations and explain your reasoning at each step.\",\n", - ")\n", - "```\n", - "\n", - "Both skill types are passed to `AgentSkills` in the same `skills` list — you can freely mix file-based and programmatic skills:\n", - "\n", - "```python\n", - "from strands import AgentSkills\n", - "\n", - "plugin = AgentSkills(skills=[\"./skills/weather-reporter\", math_tutor])\n", - "```\n", - "\n", - "The `math-tutor` skill will be defined programmatically in the agent script below.\n" - ] - }, - { - "cell_type": "markdown", - "id": "b8c9d0e1f2a3b4c5", - "metadata": {}, - "source": [ - "## Local Agent Experimentation\n", - "\n", - "Before deploying to AgentCore Runtime, we'll run the agent locally to verify that the skills work as expected.\n", - "\n", - "The agent uses `BedrockModel` with Claude Haiku 4.5 and loads skills from the `skills/` directory via the `AgentSkills` plugin. Running locally lets you iterate quickly on skill definitions and agent behavior without a full deployment cycle.\n" - ] - }, - { - "cell_type": "markdown", - "id": "f2a3b4c5d6e7f8a9", - "metadata": {}, - "source": [ - "## Deploying to Amazon Bedrock AgentCore Runtime\n", - "\n", - "Now that we've verified the agent works locally, we'll deploy it to Amazon Bedrock AgentCore Runtime. The deployment version of the agent script wraps the same logic with `BedrockAgentCoreApp`, which exposes `/invocations` and `/ping` HTTP endpoints.\n", - "\n", - "The `bedrock-agentcore-starter-toolkit` packages the agent script, the `skills/` directory, and all dependencies into a Docker container, pushes it to Amazon ECR, and creates the AgentCore Runtime endpoint automatically.\n", - "\n", - "> **Note:** Make sure Docker or Finch is running before proceeding with deployment.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "636a58fb", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import boto3\n", - "\n", - "AWS_PROFILE = \"REPLACE-ME\"\n", - "REGION = \"REPLACE-ME\"\n", - "\n", - "os.environ[\"AWS_PROFILE\"] = AWS_PROFILE\n", - "os.environ[\"AWS_DEFAULT_REGION\"] = REGION\n", - "\n", - "# Force boto3 to use the new profile by resetting the default session\n", - "boto3.setup_default_session(profile_name=AWS_PROFILE, region_name=REGION)\n", - "\n", - "# Verify credentials are valid\n", - "identity = boto3.client(\"sts\").get_caller_identity()\n", - "print(f\"Account: {identity['Account']}, Region: {REGION}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a3b4c5d6e7f8a9b0", - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile agent_with_skills.py\n", - "from strands import Agent, tool, AgentSkills, Skill\n", - "from strands_tools import calculator\n", - "from strands.models import BedrockModel\n", - "from bedrock_agentcore.runtime import BedrockAgentCoreApp\n", - "\n", - "@tool\n", - "def weather() -> dict:\n", - " \"\"\"Get current weather information.\"\"\"\n", - " return {\n", - " \"condition\": \"sunny\",\n", - " \"temp_f\": 75,\n", - " \"temp_c\": 24,\n", - " \"humidity\": 45,\n", - " \"wind_mph\": 10\n", - " }\n", - "\n", - "# Programmatic skill — defined inline, no SKILL.md file needed\n", - "math_tutor = Skill(\n", - " name=\"math-tutor\",\n", - " description=\"Solve math problems step-by-step, showing all work and explaining reasoning\",\n", - " instructions=(\n", - " \"You are a patient and thorough math tutor. When solving math problems:\\n\"\n", - " \"1. Break down the problem into clear, numbered steps.\\n\"\n", - " \"2. Show all intermediate calculations — never skip steps.\\n\"\n", - " \"3. Explain your reasoning at each step, identifying the mathematical concept applied.\\n\"\n", - " \"4. Use the calculator tool for arithmetic to ensure accuracy.\\n\"\n", - " \"5. Verify your answer against the original problem.\\n\"\n", - " \"6. Summarize with a clear final answer.\"\n", - " ),\n", - ")\n", - "\n", - "model = BedrockModel(model_id=\"global.anthropic.claude-haiku-4-5-20251001-v1:0\")\n", - "# Mix file-based skill (weather-reporter) with programmatic skill (math_tutor)\n", - "skills = AgentSkills(skills=[\"./skills/weather-reporter\", math_tutor])\n", - "agent = Agent(model=model, tools=[calculator, weather], plugins=[skills])\n", - "\n", - "app = BedrockAgentCoreApp()\n", - "\n", - "@app.entrypoint\n", - "def invoke_agent(payload: dict) -> str:\n", - " response = agent(payload.get(\"prompt\", \"\"))\n", - " return response.message['content'][0]['text']\n", - "\n", - "if __name__ == \"__main__\":\n", - " app.run()\n" - ] - }, - { - "cell_type": "markdown", - "id": "b4c5d6e7f8a9b0c1", - "metadata": {}, - "source": [ - "## Configure and Launch the AgentCore Runtime\n", - "\n", - "Use the `Runtime` class from `bedrock-agentcore-starter-toolkit` to configure the deployment. Setting `auto_create_execution_role=True` and `auto_create_ecr=True` lets the toolkit create the required IAM role and ECR repository automatically.\n", - "\n", - "The `launch()` call builds the Docker image, pushes it to ECR, and creates the AgentCore Runtime endpoint. This may take a few minutes.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c5d6e7f8a9b0c1d2", - "metadata": {}, - "outputs": [], - "source": [ - "import boto3\n", - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "\n", - "agentcore_runtime = Runtime()\n", - "agentcore_runtime.configure(\n", - " entrypoint=\"agent_with_skills.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " agent_name=\"strands_skills_tutorial\",\n", - " region=REGION\n", - ")\n", - "print(\"Configuration complete.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d6e7f8a9b0c1d2e3", - "metadata": {}, - "outputs": [], - "source": [ - "launch_result = agentcore_runtime.launch()\n", - "print(\"Launch initiated. Waiting for runtime to become READY...\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e7f8a9b0c1d2e3f4", - "metadata": {}, - "outputs": [], - "source": [ - "import time\n", - "\n", - "terminal_states = [\"READY\", \"CREATE_FAILED\", \"UPDATE_FAILED\", \"DELETE_FAILED\"]\n", - "\n", - "status_response = agentcore_runtime.status()\n", - "current_status = status_response.endpoint[\"status\"]\n", - "print(f\"Status: {current_status}\")\n", - "\n", - "while current_status not in terminal_states:\n", - " time.sleep(30)\n", - " status_response = agentcore_runtime.status()\n", - " current_status = status_response.endpoint[\"status\"]\n", - " print(f\"Status: {current_status}\")\n", - "\n", - "if current_status == \"READY\":\n", - " print(\"Runtime is READY. Proceeding to invocation.\")\n", - "else:\n", - " print(f\"Deployment ended with status: {current_status}. Check CloudWatch logs for details.\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "3dd74732", - "metadata": {}, - "source": [ - "## Test Weather Skill" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8a9b0c1d2e3f4a5", - "metadata": {}, - "outputs": [], - "source": [ - "# Invoke the deployed runtime with a prompt that triggers skill activation\n", - "invoke_payload = {\"prompt\": \"What's the weather like today? Give me a full report with emoji and activity recommendations.\"}\n", - "response = agentcore_runtime.invoke(invoke_payload)\n", - "response_text = \"\".join(response[\"response\"])\n", - "print(response_text)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5f8cd417", - "metadata": {}, - "outputs": [], - "source": [ - "from IPython.display import Markdown, display\n", - "\n", - "# Extract and display the response text\n", - "response_text = \"\".join(response[\"response\"])\n", - "# Strip surrounding quotes and replace escaped newlines\n", - "response_text = response_text.strip('\"').replace('\\\\n', '\\n')\n", - "display(Markdown(response_text))" - ] - }, - { - "cell_type": "markdown", - "id": "2d9e7cbd", - "metadata": {}, - "source": [ - "## Test Math Skill" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2dcd0fb0", - "metadata": {}, - "outputs": [], - "source": [ - "# Invoke the deployed runtime with a prompt that triggers skill activation\n", - "invoke_payload = {\"prompt\": \"help me solve 23+458*89\"}\n", - "response = agentcore_runtime.invoke(invoke_payload)\n", - "response_text = \"\".join(response[\"response\"])\n", - "print(response_text)" - ] - }, - { - "cell_type": "markdown", - "id": "b0c1d2e3f4a5b6c7", - "metadata": {}, - "source": [ - "## Cleanup\n", - "\n", - "Let's now clean up the AgentCore Runtime and associated resources. We delete the runtime first to avoid undesired costs, then clean up supporting resources like the ECR repository." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c1d2e3f4a5b6c7d8", - "metadata": {}, - "outputs": [], - "source": [ - "import boto3\n", - "\n", - "agent_name = \"strands_skills_tutorial\"\n", - "\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=REGION)\n", - "\n", - "# Look up agent_id by name in case this cell runs independently\n", - "runtimes = agentcore_control_client.list_agent_runtimes()\n", - "agent_id = next(\n", - " (r[\"agentRuntimeId\"] for r in runtimes[\"agentRuntimes\"] if r[\"agentRuntimeName\"] == agent_name),\n", - " None\n", - ")\n", - "\n", - "if not agent_id:\n", - " print(f\"⚠️ No runtime found with name '{agent_name}'\")\n", - "else:\n", - " # Delete the AgentCore Runtime\n", - " try:\n", - " agentcore_control_client.delete_agent_runtime(agentRuntimeId=agent_id)\n", - " print(f\"✅ Runtime '{agent_name}' ({agent_id}) deleted\")\n", - " except Exception as e:\n", - " print(f\"⚠️ Failed to delete runtime: {e}\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d2e3f4a5b6c7d8e9", - "metadata": {}, - "outputs": [], - "source": [ - "ecr_client = boto3.client('ecr', region_name=REGION)\n", - "REPO_NAME = \"bedrock-agentcore-strands_skills_tutorial\"\n", - "# REPO_NAME = launch_result.ecr_uri.split('/')[1]\n", - "\n", - "# Delete the ECR repository created during deployment\n", - "try:\n", - " ecr_client.delete_repository(\n", - " repositoryName=REPO_NAME,\n", - " force=True\n", - " )\n", - " print(f\"✅ ECR repository '{REPO_NAME}' deleted\")\n", - "except Exception as e:\n", - " print(f\"⚠️ Failed to delete ECR repository: {e}\")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.11" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + "cells": [ + { + "cell_type": "markdown", + "id": "a1b2c3d4e5f6a7b8", + "metadata": {}, + "source": [ + "# Strands Agent with AgentSkills Plugin on Amazon Bedrock AgentCore Runtime\n", + "\n", + "## Overview\n", + "\n", + "This tutorial demonstrates how to build a Strands agent using the `AgentSkills` plugin for on-demand specialized instructions, run it locally, and deploy it to Amazon Bedrock AgentCore Runtime.\n", + "\n", + "The `AgentSkills` plugin supports two ways to define skills: **file-based** (a `SKILL.md` file with YAML frontmatter) and **programmatic** (a `Skill` object defined inline in Python). Both approaches can be mixed in the same agent.\n", + "\n", + "We will walk through two example skills — a **weather-reporter** skill defined as a file-based `SKILL.md`, and a **math-tutor** skill defined programmatically using the `Skill` class. We will first experiment locally, then deploy the agent to AgentCore Runtime.\n", + "\n", + "### Tutorial Details\n", + "\n", + "| Field | Value |\n", + "|---|---|\n", + "| Tutorial type | Jupyter Notebook |\n", + "| Agent type | Strands Agent with AgentSkills Plugin |\n", + "| Agentic Framework | Strands Agents |\n", + "| LLM model | Amazon Bedrock (Claude Haiku 4.5) |\n", + "| Tutorial components | Skills (SKILL.md), AgentSkills Plugin, Local Experimentation, AgentCore Runtime Deployment |\n", + "| Tutorial vertical | General / Developer Education |\n", + "| Example complexity | Intermediate |\n", + "| SDK used | bedrock-agentcore-starter-toolkit, strands-agents |\n", + "\n", + "### Tutorial Architecture\n", + "\n", + "The agent uses the `AgentSkills` plugin which scans a `skills/` directory. Each skill is a `SKILL.md` file with YAML frontmatter (`name`, `description`, `allowed-tools`) and markdown instructions. The agent selects the appropriate skill based on the user's request.\n", + "\n", + "Two example skills are provided:\n", + "- **weather-reporter**: Defined as a file-based `SKILL.md`. Paired with a custom `@tool` weather function, formats weather information with emoji, temperature ranges, and activity recommendations.\n", + "- **math-tutor**: Defined programmatically using the `Skill` class (no file needed). Paired with the `calculator` tool from `strands-agents-tools`, solves math problems step-by-step showing all work.\n", + "\n", + "### Tutorial Key Features\n", + "\n", + "* Two skill definition approaches: file-based (`SKILL.md`) and programmatic (`Skill` class)\n", + "* Mixing file-based and programmatic skills in a single `AgentSkills` plugin\n", + "* On-demand skill activation based on user intent\n", + "* Local experimentation before cloud deployment\n", + "* Seamless deployment to Amazon Bedrock AgentCore Runtime\n" + ] + }, + { + "cell_type": "markdown", + "id": "b2c3d4e5f6a7b8c9", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "To execute this tutorial you will need:\n", + "* Python 3.10+\n", + "* AWS credentials configured (`aws configure`)\n", + "* Amazon Bedrock AgentCore SDK (`bedrock-agentcore`, `bedrock-agentcore-starter-toolkit`)\n", + "* Strands Agents (`strands-agents`, `strands-agents-tools`)\n", + "* Access to Amazon Bedrock models (Claude Haiku 4.5)\n", + "* Docker or Finch (for AgentCore Runtime deployment)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3d4e5f6a7b8c9d0", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install -r requirements.txt" + ] + }, + { + "cell_type": "markdown", + "id": "d4e5f6a7b8c9d0e1", + "metadata": {}, + "source": [ + "## Skill Creation Approaches\n", + "\n", + "The `AgentSkills` plugin supports two ways to define skills. This tutorial demonstrates both.\n", + "\n", + "### Approach 1: File-based skill (SKILL.md)\n", + "\n", + "A file-based skill is a directory containing a `SKILL.md` file with YAML frontmatter and a markdown instruction body. This approach is ideal for skills you want to version-control, share across projects, or edit without touching Python code.\n", + "\n", + "```\n", + "skills/\n", + "└── weather-reporter/\n", + " └── SKILL.md\n", + "```\n", + "\n", + "**SKILL.md format:**\n", + "```markdown\n", + "---\n", + "name: skill-name\n", + "description: Short description used by the agent to decide when to activate this skill\n", + "allowed-tools:\n", + " - tool_name\n", + "---\n", + "\n", + "# Skill Instructions\n", + "\n", + "Markdown body with behavioral instructions for the agent.\n", + "```\n", + "\n", + "The YAML frontmatter fields:\n", + "- `name`: Unique identifier for the skill (kebab-case)\n", + "- `description`: Human-readable description used by the agent to decide when to activate the skill\n", + "- `allowed-tools`: List of tool names the skill is permitted to use\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e5f6a7b8c9d0e1f2", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "os.makedirs(\"skills/weather-reporter\", exist_ok=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6a7b8c9d0e1f2a3", + "metadata": {}, + "outputs": [], + "source": [ + "%%writefile skills/weather-reporter/SKILL.md\n", + "---\n", + "name: weather-reporter\n", + "description: Format weather information with emoji, temperature ranges, and activity recommendations\n", + "allowed-tools:\n", + " - weather\n", + "---\n", + "\n", + "# Weather Reporter Instructions\n", + "\n", + "You are a friendly weather reporter. When presenting weather information:\n", + "\n", + "1. **Use weather emoji** to make the report visually engaging:\n", + " - ☀️ for sunny/clear conditions\n", + " - 🌧️ for rain\n", + " - ⛅ for partly cloudy\n", + " - 🌩️ for thunderstorms\n", + " - ❄️ for snow\n", + " - 🌫️ for fog/mist\n", + "\n", + "2. **Include temperature ranges** in both Fahrenheit and Celsius\n", + "\n", + "3. **Provide activity recommendations** based on the conditions:\n", + " - Suggest outdoor activities for good weather\n", + " - Recommend indoor alternatives for bad weather\n", + " - Include clothing suggestions (e.g., \"bring an umbrella\", \"wear a light jacket\")\n", + "\n", + "4. **Format your response** as a friendly weather report with clear sections for current conditions and recommendations.\n" + ] + }, + { + "cell_type": "markdown", + "id": "a7b8c9d0e1f2a3b4", + "metadata": {}, + "source": [ + "### Approach 2: Programmatic skill (Skill class)\n", + "\n", + "A programmatic skill is defined inline in Python using the `Skill` class — no directory or file needed. This is convenient for simple skills, dynamic skill generation, or when you want to keep everything in one script.\n", + "\n", + "```python\n", + "from strands import Skill\n", + "\n", + "math_tutor = Skill(\n", + " name=\"math-tutor\",\n", + " description=\"Solve math problems step-by-step, showing all work\",\n", + " instructions=\"Break down math problems into clear steps. Show all intermediate calculations and explain your reasoning at each step.\",\n", + ")\n", + "```\n", + "\n", + "Both skill types are passed to `AgentSkills` in the same `skills` list — you can freely mix file-based and programmatic skills:\n", + "\n", + "```python\n", + "from strands import AgentSkills\n", + "\n", + "plugin = AgentSkills(skills=[\"./skills/weather-reporter\", math_tutor])\n", + "```\n", + "\n", + "The `math-tutor` skill will be defined programmatically in the agent script below.\n" + ] + }, + { + "cell_type": "markdown", + "id": "b8c9d0e1f2a3b4c5", + "metadata": {}, + "source": [ + "## Local Agent Experimentation\n", + "\n", + "Before deploying to AgentCore Runtime, we'll run the agent locally to verify that the skills work as expected.\n", + "\n", + "The agent uses `BedrockModel` with Claude Haiku 4.5 and loads skills from the `skills/` directory via the `AgentSkills` plugin. Running locally lets you iterate quickly on skill definitions and agent behavior without a full deployment cycle.\n" + ] + }, + { + "cell_type": "markdown", + "id": "f2a3b4c5d6e7f8a9", + "metadata": {}, + "source": [ + "## Deploying to Amazon Bedrock AgentCore Runtime\n", + "\n", + "Now that we've verified the agent works locally, we'll deploy it to Amazon Bedrock AgentCore Runtime. The deployment version of the agent script wraps the same logic with `BedrockAgentCoreApp`, which exposes `/invocations` and `/ping` HTTP endpoints.\n", + "\n", + "The `bedrock-agentcore-starter-toolkit` packages the agent script, the `skills/` directory, and all dependencies into a Docker container, pushes it to Amazon ECR, and creates the AgentCore Runtime endpoint automatically.\n", + "\n", + "> **Note:** Make sure Docker or Finch is running before proceeding with deployment.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "636a58fb", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import boto3\n", + "\n", + "AWS_PROFILE = \"REPLACE-ME\"\n", + "REGION = \"REPLACE-ME\"\n", + "\n", + "os.environ[\"AWS_PROFILE\"] = AWS_PROFILE\n", + "os.environ[\"AWS_DEFAULT_REGION\"] = REGION\n", + "\n", + "# Force boto3 to use the new profile by resetting the default session\n", + "boto3.setup_default_session(profile_name=AWS_PROFILE, region_name=REGION)\n", + "\n", + "# Verify credentials are valid\n", + "identity = boto3.client(\"sts\").get_caller_identity()\n", + "print(f\"Account: {identity['Account']}, Region: {REGION}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a3b4c5d6e7f8a9b0", + "metadata": {}, + "outputs": [], + "source": [ + "%%writefile agent_with_skills.py\n", + "from strands import Agent, tool, AgentSkills, Skill\n", + "from strands_tools import calculator\n", + "from strands.models import BedrockModel\n", + "from bedrock_agentcore.runtime import BedrockAgentCoreApp\n", + "\n", + "@tool\n", + "def weather() -> dict:\n", + " \"\"\"Get current weather information.\"\"\"\n", + " return {\n", + " \"condition\": \"sunny\",\n", + " \"temp_f\": 75,\n", + " \"temp_c\": 24,\n", + " \"humidity\": 45,\n", + " \"wind_mph\": 10\n", + " }\n", + "\n", + "# Programmatic skill — defined inline, no SKILL.md file needed\n", + "math_tutor = Skill(\n", + " name=\"math-tutor\",\n", + " description=\"Solve math problems step-by-step, showing all work and explaining reasoning\",\n", + " instructions=(\n", + " \"You are a patient and thorough math tutor. When solving math problems:\\n\"\n", + " \"1. Break down the problem into clear, numbered steps.\\n\"\n", + " \"2. Show all intermediate calculations — never skip steps.\\n\"\n", + " \"3. Explain your reasoning at each step, identifying the mathematical concept applied.\\n\"\n", + " \"4. Use the calculator tool for arithmetic to ensure accuracy.\\n\"\n", + " \"5. Verify your answer against the original problem.\\n\"\n", + " \"6. Summarize with a clear final answer.\"\n", + " ),\n", + ")\n", + "\n", + "model = BedrockModel(model_id=\"global.anthropic.claude-haiku-4-5-20251001-v1:0\")\n", + "# Mix file-based skill (weather-reporter) with programmatic skill (math_tutor)\n", + "skills = AgentSkills(skills=[\"./skills/weather-reporter\", math_tutor])\n", + "agent = Agent(model=model, tools=[calculator, weather], plugins=[skills])\n", + "\n", + "app = BedrockAgentCoreApp()\n", + "\n", + "@app.entrypoint\n", + "def invoke_agent(payload: dict) -> str:\n", + " response = agent(payload.get(\"prompt\", \"\"))\n", + " return response.message['content'][0]['text']\n", + "\n", + "if __name__ == \"__main__\":\n", + " app.run()\n" + ] + }, + { + "cell_type": "markdown", + "id": "b4c5d6e7f8a9b0c1", + "metadata": {}, + "source": [ + "## Configure and Launch the AgentCore Runtime\n", + "\n", + "Use the `Runtime` class from `bedrock-agentcore-starter-toolkit` to configure the deployment. Setting `auto_create_execution_role=True` and `auto_create_ecr=True` lets the toolkit create the required IAM role and ECR repository automatically.\n", + "\n", + "The `launch()` call builds the Docker image, pushes it to ECR, and creates the AgentCore Runtime endpoint. This may take a few minutes.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5d6e7f8a9b0c1d2", + "metadata": {}, + "outputs": [], + "source": [ + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", + "\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=REGION)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam', region_name=region)\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10)\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name, CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for root, dirs, fls in os.walk('.'):\n", + " for fname in fls:\n", + " fpath = os.path.join(root, fname)\n", + " arcname = os.path.relpath(fpath, '.')\n", + " if not arcname.startswith('.') and '__pycache__' not in arcname:\n", + " zf.write(fpath, arcname)\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "account_id = boto3.client('sts', region_name=REGION).get_caller_identity()['Account']\n", + "agent_name = \"strands_skills_tutorial\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, REGION, account_id)\n", + "\n", + "# Package and upload agent code (includes skills/ directory)\n", + "bucket_name, s3_key = package_and_upload_to_s3(agent_name, None, REGION, account_id)\n", + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6e7f8a9b0c1d2e3", + "metadata": {}, + "outputs": [], + "source": [ + "# Create the AgentCore Runtime using CodeZip deployment\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['agent_with_skills.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created: {agent_runtime_id}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e7f8a9b0c1d2e3f4", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", + "status = create_response.get('status', 'CREATING')\n", + "while status not in end_status:\n", + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "3dd74732", + "metadata": {}, + "source": [ + "## Test Weather Skill" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8a9b0c1d2e3f4a5", + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Markdown, display\n", + "import boto3, json\n", + "\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=REGION)\n", + "invoke_payload = {\"prompt\": \"What's the weather like today? Give me a full report with emoji and activity recommendations.\"}\n", + "response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps(invoke_payload)\n", + ")\n", + "events = [e for e in response.get(\"response\", [])]\n", + "response_text = json.loads(events[0].decode(\"utf-8\")) if events else \"\"\n", + "response_text = response_text.strip('\"').replace('\\\\n', '\\n')\n", + "display(Markdown(response_text))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f8cd417", + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Markdown, display\n", + "\n", + "# Extract and display the response text\n", + "response_text = \"\".join(response[\"response\"])\n", + "# Strip surrounding quotes and replace escaped newlines\n", + "response_text = response_text.strip('\"').replace('\\\\n', '\\n')\n", + "display(Markdown(response_text))" + ] + }, + { + "cell_type": "markdown", + "id": "2d9e7cbd", + "metadata": {}, + "source": [ + "## Test Math Skill" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2dcd0fb0", + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Markdown, display\n", + "import boto3, json\n", + "\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=REGION)\n", + "invoke_payload = {\"prompt\": \"What's the weather like today? Give me a full report with emoji and activity recommendations.\"}\n", + "response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps(invoke_payload)\n", + ")\n", + "events = [e for e in response.get(\"response\", [])]\n", + "response_text = json.loads(events[0].decode(\"utf-8\")) if events else \"\"\n", + "response_text = response_text.strip('\"').replace('\\\\n', '\\n')\n", + "display(Markdown(response_text))\n" + ] + }, + { + "cell_type": "markdown", + "id": "b0c1d2e3f4a5b6c7", + "metadata": {}, + "source": [ + "## Cleanup\n", + "\n", + "Let's now clean up the AgentCore Runtime and associated resources. We delete the runtime first to avoid undesired costs, then clean up supporting resources like the ECR repository." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c1d2e3f4a5b6c7d8", + "metadata": {}, + "outputs": [], + "source": [ + "import boto3\n", + "\n", + "agent_name = \"strands_skills_tutorial\"\n", + "\n", + "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=REGION)\n", + "\n", + "# Look up agent_id by name in case this cell runs independently\n", + "runtimes = agentcore_control_client.list_agent_runtimes()\n", + "agent_id = next(\n", + " (r[\"agentRuntimeId\"] for r in runtimes[\"agentRuntimes\"] if r[\"agentRuntimeName\"] == agent_name),\n", + " None\n", + ")\n", + "\n", + "if not agent_id:\n", + " print(f\"⚠️ No runtime found with name '{agent_name}'\")\n", + "else:\n", + " # Delete the AgentCore Runtime\n", + " try:\n", + " agentcore_control_client.delete_agent_runtime(agentRuntimeId=agent_id)\n", + " print(f\"✅ Runtime '{agent_name}' ({agent_id}) deleted\")\n", + " except Exception as e:\n", + " print(f\"⚠️ Failed to delete runtime: {e}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d2e3f4a5b6c7d8e9", + "metadata": {}, + "outputs": [], + "source": [ + "import boto3\n", + "\n", + "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=REGION)\n", + "s3_client = boto3.client('s3', region_name=REGION)\n", + "\n", + "# Delete the AgentCore Runtime\n", + "runtimes = agentcore_control_client.list_agent_runtimes()\n", + "agent_id = next(\n", + " (r[\"agentRuntimeId\"] for r in runtimes[\"agentRuntimes\"] if r[\"agentRuntimeName\"] == agent_name),\n", + " None\n", + ")\n", + "\n", + "if not agent_id:\n", + " print(f\"No runtime found with name '{agent_name}'\")\n", + "else:\n", + " try:\n", + " agentcore_control_client.delete_agent_runtime(agentRuntimeId=agent_id)\n", + " print(f\"Runtime '{agent_name}' ({agent_id}) deleted\")\n", + " except Exception as e:\n", + " print(f\"Failed to delete runtime: {e}\")\n", + "\n", + "# Delete S3 deployment artifact\n", + "try:\n", + " s3_client.delete_object(Bucket=bucket_name, Key=s3_key)\n", + " print(f\"S3 artifact deleted\")\n", + "except Exception as e:\n", + " print(f\"Failed to delete S3 artifact: {e}\")\n", + "\n", + "print(\"Cleanup complete.\")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/README.md b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/README.md index 16dd994cc..c9309fa50 100644 --- a/01-tutorials/01-AgentCore-runtime/01-hosting-agent/README.md +++ b/01-tutorials/01-AgentCore-runtime/01-hosting-agent/README.md @@ -15,7 +15,9 @@ The Amazon Bedrock AgentCore Python SDK acts as a wrapper that: - **HTTP Protocol**: Traditional request/response REST API endpoints - **MCP Protocol**: Model Context Protocol for tools and agent servers -### Service Architecture +--- + +## Architecture When hosting agents, the SDK automatically: @@ -26,15 +28,21 @@ When hosting agents, the SDK automatically: ![Hosting agent](images/hosting_agent_python_sdk.png) -Once your agent is prepared for deployment on AgentCore Runtime, you can use the Amazon Bedrock AgentCore StarterKit to deploy it to deploy it to AgentCore Runtime. +Once your agent is prepared for deployment on AgentCore Runtime, you deploy it using the Amazon Bedrock AgentCore SDK and boto3. The deployment uses **CodeZip** — your Python code is zipped, uploaded to S3, and the runtime is created via `create_agent_runtime`. -With the Starter Kit you can configure your agent deployment, launch it to create an Amazon ECR repository with the Agent's configuration and the AgentCore Runtime endpoint and invoke the created endpoint for validation. +![RuntimeArchitecture](../images/runtime_architecture.png) -![StarterKit](../images/runtime_overview.png) +--- -Once deployed, your AgentCore Runtime architecture in AWS looks as following: +## What's This Feature -![RuntimeArchitecture](../images/runtime_architecture.png) +Amazon Bedrock AgentCore Runtime provides: + +- **Serverless deployment** of Python agents — no container management required (CodeZip) +- **Session isolation** — each user session runs in an isolated microVM +- **Session lifecycle management** — stop sessions to release resources, configure idle timeouts +- **Framework agnostic** — works with Strands, LangGraph, CrewAI, or any Python framework +- **Model flexible** — supports Amazon Bedrock models, OpenAI, Azure OpenAI, and any LiteLLM provider ## Tutorial Examples @@ -47,6 +55,69 @@ This tutorial includes four hands-on examples to get you started: | **[03-strands-with-openai-model](03-strands-with-openai-model)** | Strands Agents | OpenAI | Integration with external LLM providers | | **[06-strands-with-skills](06-strands-with-skills)** | Strands Agents | Amazon Bedrock | Skills-based agent hosting with AgentSkills plugin | +--- + +## CLI Commands + +> **CLI version**: `agentcore@0.11.0` +> +> Install or update: `npm install -g @aws/agentcore@0.11.0` + +The `agentcore` CLI provides a streamlined workflow for creating and deploying AgentCore Runtime projects. + +### Full workflow example (Strands + Bedrock) + +```bash +# 1. Create project +agentcore create \ + --name myagent \ + --framework Strands \ + --model-provider Bedrock \ + --build CodeZip \ + --skip-git --skip-install --json + +# 2. Add your agent code +cp my_agent.py myagent/app/myagent/main.py + +# 3. Deploy +cd myagent +agentcore deploy -y --json + +# 4. Check status +agentcore status --json + +# 5. Invoke +agentcore invoke "What is the weather in Athens?" --json + +# 6. View logs +agentcore logs --since 30m -n 20 +``` + +--- + +## Cleanup + +**Using boto3** (recommended when deployed via notebook): + +```python +import boto3 +agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region) +agentcore_control.delete_agent_runtime(agentRuntimeId=agent_runtime_id) + +# Also clean up the S3 deployment artifact +s3 = boto3.client('s3', region_name=region) +s3.delete_object(Bucket=bucket_name, Key=s3_key) +``` + +**Using CLI** (when deployed via `agentcore deploy`): + +```bash +agentcore remove agent --name myagent --json +agentcore deploy -y --json +``` + +--- + ## Key Benefits - **Framework Agnostic**: Works with any Python-based agent framework diff --git a/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/hosting_mcp_server.ipynb b/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/hosting_mcp_server.ipynb index 896e0cf27..131f4f4c0 100644 --- a/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/hosting_mcp_server.ipynb +++ b/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/hosting_mcp_server.ipynb @@ -276,7 +276,7 @@ "source": [ "## Configuring AgentCore Runtime Deployment\n", "\n", - "Next we will use our starter toolkit to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the starter kit to auto create the Amazon ECR repository on launch.\n", + "Next we will use our AgentCore SDK to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the AgentCore SDK to auto create the Amazon ECR repository on launch.\n", "\n", "During the configure step, your docker file will be generated based on your application code\n", "\n", @@ -292,40 +292,100 @@ "metadata": {}, "outputs": [], "source": [ + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", "import os\n", - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "\n", - "print(f\"Using AWS region: {region}\")\n", - "\n", - "required_files = ['mcp_server.py', 'requirements.txt']\n", - "for file in required_files:\n", - " if not os.path.exists(file):\n", - " raise FileNotFoundError(f\"Required file {file} not found\")\n", - "print(\"All required files found ✓\")\n", - "\n", - "agentcore_runtime = Runtime()\n", + "import time\n", "\n", - "auth_config = {\n", - " \"customJWTAuthorizer\": {\n", - " \"allowedClients\": [\n", - " cognito_config['client_id']\n", - " ],\n", - " \"discoveryUrl\": cognito_config['discovery_url'],\n", + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", " }\n", - "}\n", - "\n", - "print(\"Configuring AgentCore Runtime...\")\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"mcp_server.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " authorizer_configuration=auth_config,\n", - " protocol=\"MCP\",\n", - " agent_name=tool_name\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"agentcore_runtime\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"mcp_server.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "print(\"Configuration completed ✓\")" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -349,12 +409,24 @@ "metadata": {}, "outputs": [], "source": [ - "print(\"Launching MCP server to AgentCore Runtime...\")\n", - "print(\"This may take several minutes...\")\n", - "launch_result = agentcore_runtime.launch()\n", - "print(\"Launch completed ✓\")\n", - "print(f\"Agent ARN: {launch_result.agent_arn}\")\n", - "print(f\"Agent ID: {launch_result.agent_id}\")" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['mcp_server.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -944,50 +1016,8 @@ "metadata": {}, "outputs": [], "source": [ - "# --- Cleanup Resources ---\n", - "import boto3\n", - "\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", - "ecr_client = boto3.client('ecr', region_name=region)\n", - "\n", - "# Step 1: Delete Secrets Manager secret first to minimize credential exposure window\n", - "try:\n", - " secrets_client.delete_secret(\n", - " SecretId='mcp_server/cognito/credentials',\n", - " ForceDeleteWithoutRecovery=True\n", - " )\n", - " print(\"✅ Secrets Manager secret deleted\")\n", - "except secrets_client.exceptions.ResourceNotFoundException:\n", - " print(\"ℹ️ Secrets Manager secret not found\")\n", - "\n", - "# Step 2: Delete Parameter Store parameter\n", - "try:\n", - " ssm_client.delete_parameter(Name='/mcp_server/runtime/agent_arn')\n", - " print(\"✅ Parameter Store parameter deleted\")\n", - "except ssm_client.exceptions.ParameterNotFound:\n", - " print(\"ℹ️ Parameter Store parameter not found\")\n", - "\n", - "# Step 3: Delete the agent runtime to stop incurring costs\n", - "# AgentCore Runtime costs are based on vCPU and Memory\n", - "try:\n", - " agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result.agent_id,\n", - " )\n", - " print(f\"✅ Agent runtime '{launch_result.agent_id}' deleted\")\n", - "except Exception as e:\n", - " print(f\"⚠️ Failed to delete agent runtime: {e}\")\n", - "\n", - "# Step 4: Delete the ECR repository\n", - "try:\n", - " ecr_client.delete_repository(\n", - " repositoryName=launch_result.ecr_uri.split('/')[1],\n", - " force=True\n", - " )\n", - " print(f\"✅ ECR repository '{launch_result.ecr_uri.split('/')[1]}' deleted\")\n", - "except Exception as e:\n", - " print(f\"⚠️ Failed to delete ECR repository: {e}\")\n", - "\n", - "print(\"\\n✅ Cleanup completed successfully!\")" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -1043,4 +1073,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/hosting_mcp_server_iam_auth.ipynb b/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/hosting_mcp_server_iam_auth.ipynb index 5ae1b27f4..71962db6d 100644 --- a/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/hosting_mcp_server_iam_auth.ipynb +++ b/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/hosting_mcp_server_iam_auth.ipynb @@ -248,7 +248,7 @@ "source": [ "## Configuring AgentCore Runtime Deployment\n", "\n", - "Next we will use our starter toolkit to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the starter kit to auto create the Amazon ECR repository on launch.\n", + "Next we will use our AgentCore SDK to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the AgentCore SDK to auto create the Amazon ECR repository on launch.\n", "\n", "During the configure step, your docker file will be generated based on your application code\n", "\n", @@ -264,30 +264,100 @@ "metadata": {}, "outputs": [], "source": [ + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", "import os\n", - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "\n", - "print(f\"Using AWS region: {region}\")\n", + "import time\n", "\n", - "required_files = [\"mcp_server.py\", \"requirements.txt\"]\n", - "for file in required_files:\n", - " if not os.path.exists(file):\n", - " raise FileNotFoundError(f\"Required file {file} not found\")\n", - "print(\"All required files found ✓\")\n", - "\n", - "agentcore_runtime = Runtime()\n", - "\n", - "print(\"Configuring AgentCore Runtime...\")\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"mcp_server.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " protocol=\"MCP\",\n", - " agent_name=tool_name,\n", + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"agentcore_runtime\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"mcp_server.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "print(\"Configuration completed ✓\")" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -311,12 +381,24 @@ "metadata": {}, "outputs": [], "source": [ - "print(\"Launching MCP server to AgentCore Runtime...\")\n", - "print(\"This may take several minutes...\")\n", - "launch_result = agentcore_runtime.launch()\n", - "print(\"Launch completed ✓\")\n", - "print(f\"Agent ARN: {launch_result.agent_arn}\")\n", - "print(f\"Agent ID: {launch_result.agent_id}\")" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['mcp_server.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -1208,40 +1290,8 @@ "metadata": {}, "outputs": [], "source": [ - "# --- Cleanup Resources ---\n", - "import boto3\n", - "\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", - "ecr_client = boto3.client('ecr', region_name=region)\n", - "\n", - "# Step 1: Delete Parameter Store parameter first to minimize credential exposure window\n", - "try:\n", - " ssm_client.delete_parameter(Name='/mcp_server/runtime_iam/agent_arn')\n", - " print(\"✅ Parameter Store parameter deleted\")\n", - "except ssm_client.exceptions.ParameterNotFound:\n", - " print(\"ℹ️ Parameter Store parameter not found\")\n", - "\n", - "# Step 2: Delete the agent runtime to stop incurring costs\n", - "# AgentCore Runtime costs are based on vCPU and Memory\n", - "try:\n", - " agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result.agent_id,\n", - " )\n", - " print(f\"✅ Agent runtime '{launch_result.agent_id}' deleted\")\n", - "except Exception as e:\n", - " print(f\"⚠️ Failed to delete agent runtime: {e}\")\n", - "\n", - "# Step 3: Delete the ECR repository\n", - "try:\n", - " ecr_client.delete_repository(\n", - " repositoryName=launch_result.ecr_uri.split('/')[1],\n", - " force=True\n", - " )\n", - " print(f\"✅ ECR repository deleted\")\n", - "except Exception as e:\n", - " print(f\"⚠️ Failed to delete ECR repository: {e}\")\n", - "\n", - "print(\"\\n✅ Cleanup completed successfully!\")" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -1303,4 +1353,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/requirements.txt b/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/requirements.txt index c45f4d476..f45426f80 100644 --- a/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/requirements.txt @@ -1,4 +1,3 @@ mcp>=1.10.0 boto3 -bedrock-agentcore<=0.1.5 -bedrock-agentcore-starter-toolkit==0.1.14 \ No newline at end of file +bedrock-agentcore diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/01-streaming-agent-response/requirements.txt b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/01-streaming-agent-response/requirements.txt index e0b1cf67c..90ebb49ad 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/01-streaming-agent-response/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/01-streaming-agent-response/requirements.txt @@ -2,5 +2,4 @@ strands-agents strands-agents-tools uv boto3 -bedrock-agentcore<=0.1.5 -bedrock-agentcore-starter-toolkit==0.1.14 \ No newline at end of file +bedrock-agentcore diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/01-streaming-agent-response/streamining_agent_response.ipynb b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/01-streaming-agent-response/streamining_agent_response.ipynb index e14e14377..bd591bace 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/01-streaming-agent-response/streamining_agent_response.ipynb +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/01-streaming-agent-response/streamining_agent_response.ipynb @@ -209,7 +209,7 @@ "source": [ "### Configure AgentCore Runtime deployment\n", "\n", - "Next we will use our starter toolkit to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the starter kit to auto create the Amazon ECR repository on launch.\n", + "Next we will use our AgentCore SDK to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the AgentCore SDK to auto create the Amazon ECR repository on launch.\n", "\n", "During the configure step, your docker file will be generated based on your application code\n", "\n", @@ -225,23 +225,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "from boto3.session import Session\n", - "boto_session = Session()\n", - "region = boto_session.region_name\n", - "region\n", - "\n", - "agentcore_runtime = Runtime()\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", "\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"strands_claude_streaming.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=\"strands_claude_streaming\"\n", + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"strands_claude_streaming\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"strands_claude_streaming.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "response" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -265,7 +342,24 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result = agentcore_runtime.launch()" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['strands_claude_streaming.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -285,16 +379,14 @@ "outputs": [], "source": [ "import time\n", - "\n", - "status_response = agentcore_runtime.status()\n", - "status = status_response.endpoint['status']\n", "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", + "status = create_response.get('status', 'CREATING')\n", "while status not in end_status:\n", - " time.sleep(10)\n", - " status_response = agentcore_runtime.status()\n", - " status = status_response.endpoint['status']\n", - " print(status)\n", - "status" + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" ] }, { @@ -320,11 +412,30 @@ }, "outputs": [], "source": [ - "invoke_response = agentcore_runtime.invoke({\n", - " \"prompt\": \n", - " \"what the weather is like?\"\n", - "})\n", - "invoke_response" + "import boto3\n", + "import json\n", + "from IPython.display import Markdown, display\n", + "\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", + "\n", + "invoke_response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"How is the weather now in Athens ?\"})\n", + ")\n", + "\n", + "# Capture the runtime session ID for lifecycle management\n", + "runtime_session_id = invoke_response.get('runtimeSessionId')\n", + "print(f\"Runtime Session ID: {runtime_session_id}\")\n", + "\n", + "try:\n", + " events = []\n", + " for event in invoke_response.get(\"response\", []):\n", + " events.append(event)\n", + "except Exception as e:\n", + " events = [f\"Error reading EventStream: {e}\"]\n", + "response_text = json.loads(events[0].decode(\"utf-8\"))\n", + "display(Markdown(response_text))\n" ] }, { @@ -438,7 +549,8 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result.ecr_uri, launch_result.agent_id, launch_result.ecr_uri.split('/')[1]" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -448,23 +560,8 @@ "metadata": {}, "outputs": [], "source": [ - "agentcore_control_client = boto3.client(\n", - " 'bedrock-agentcore-control',\n", - " region_name=region\n", - ")\n", - "ecr_client = boto3.client(\n", - " 'ecr',\n", - " region_name=region\n", - ")\n", - "\n", - "runtime_delete_response = agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result.agent_id,\n", - ")\n", - "\n", - "response = ecr_client.delete_repository(\n", - " repositoryName=launch_result.ecr_uri.split('/')[1],\n", - " force=True\n", - ")" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -511,4 +608,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/02-understanding-runtime-context/requirements.txt b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/02-understanding-runtime-context/requirements.txt index e0b1cf67c..90ebb49ad 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/02-understanding-runtime-context/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/02-understanding-runtime-context/requirements.txt @@ -2,5 +2,4 @@ strands-agents strands-agents-tools uv boto3 -bedrock-agentcore<=0.1.5 -bedrock-agentcore-starter-toolkit==0.1.14 \ No newline at end of file +bedrock-agentcore diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/02-understanding-runtime-context/understanding_runtime_context.ipynb b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/02-understanding-runtime-context/understanding_runtime_context.ipynb index c85def0e7..3aba7127f 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/02-understanding-runtime-context/understanding_runtime_context.ipynb +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/02-understanding-runtime-context/understanding_runtime_context.ipynb @@ -306,7 +306,7 @@ "source": [ "### Configure AgentCore Runtime deployment\n", "\n", - "Next we will use our starter toolkit to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the starter kit to auto create the Amazon ECR repository on launch.\n", + "Next we will use our AgentCore SDK to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the AgentCore SDK to auto create the Amazon ECR repository on launch.\n", "\n", "During the configure step, your docker file will be generated based on your application code\n", "\n", @@ -322,22 +322,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "from boto3.session import Session\n", - "boto_session = Session()\n", - "region = boto_session.region_name\n", - "region\n", - "\n", - "agentcore_runtime = Runtime()\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", "\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"strands_claude_context.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=\"strands_claude_context\"\n", - ")" + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"strands_claude_context\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"strands_claude_context.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", + ")\n", + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -363,7 +441,24 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result = agentcore_runtime.launch()" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['strands_claude_context.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -382,8 +477,15 @@ "metadata": {}, "outputs": [], "source": [ - "status_response = agentcore_runtime.status()\n", - "status_response.endpoint['status']" + "import time\n", + "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", + "status = create_response.get('status', 'CREATING')\n", + "while status not in end_status:\n", + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" ] }, { @@ -426,23 +528,30 @@ }, "outputs": [], "source": [ - "import uuid\n", + "import boto3\n", "import json\n", "from IPython.display import Markdown, display\n", "\n", - "# Create a session ID for demonstrating session continuity\n", - "session_id = uuid.uuid4()\n", - "print(f\"\ud83d\udccb Starting Session 1: {session_id}\")\n", - "print(f\"\ud83d\udc64 User: Maira (ID: 1)\")\n", - "print(f\"\u2753 First question about weather\\n\")\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", "\n", - "invoke_response = agentcore_runtime.invoke({\n", - " \"prompt\": \"How is the weather outside?\",\n", - " \"user_id\": \"1\"\n", - "}, session_id=str(session_id))\n", + "invoke_response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"How is the weather now in Athens ?\"})\n", + ")\n", "\n", - "response_data = invoke_response['response'][0]\n", - "display(Markdown(response_data))" + "# Capture the runtime session ID for lifecycle management\n", + "runtime_session_id = invoke_response.get('runtimeSessionId')\n", + "print(f\"Runtime Session ID: {runtime_session_id}\")\n", + "\n", + "try:\n", + " events = []\n", + " for event in invoke_response.get(\"response\", []):\n", + " events.append(event)\n", + "except Exception as e:\n", + " events = [f\"Error reading EventStream: {e}\"]\n", + "response_text = json.loads(events[0].decode(\"utf-8\"))\n", + "display(Markdown(response_text))\n" ] }, { @@ -461,9 +570,9 @@ "outputs": [], "source": [ "# Continue with the same session ID to demonstrate session continuity\n", - "print(f\"\ud83d\udd04 Continuing Session 1: {session_id}\")\n", - "print(f\"\ud83d\udc64 Same user: Maira (ID: 1)\")\n", - "print(f\"\u2753 Follow-up question about math\\n\")\n", + "print(f\"🔄 Continuing Session 1: {session_id}\")\n", + "print(f\"👤 Same user: Maira (ID: 1)\")\n", + "print(f\"❓ Follow-up question about math\\n\")\n", "\n", "invoke_response = agentcore_runtime.invoke({\n", " \"prompt\": \"How much is 2X5?\",\n", @@ -490,9 +599,9 @@ "outputs": [], "source": [ "# Continue with the same session ID - notice how the agent remembers the previous calculation\n", - "print(f\"\ud83d\udd04 Continuing Session 1: {session_id}\")\n", - "print(f\"\ud83d\udc64 Same user: Maira (ID: 1)\")\n", - "print(f\"\u2753 Building on previous answer - demonstrates context continuity\\n\")\n", + "print(f\"🔄 Continuing Session 1: {session_id}\")\n", + "print(f\"👤 Same user: Maira (ID: 1)\")\n", + "print(f\"❓ Building on previous answer - demonstrates context continuity\\n\")\n", "\n", "invoke_response = agentcore_runtime.invoke({\n", " \"prompt\": \"and that plus 34?\",\n", @@ -538,7 +647,7 @@ " runtimeSessionId=str(session_id),\n", " qualifier='DEFAULT'\n", ")\n", - "print(f\"\u2705 Session 1 '{session_id}' stopped \u2014 microVM resources released\")" + "print(f\"✅ Session 1 '{session_id}' stopped — microVM resources released\")" ] }, { @@ -559,9 +668,9 @@ "# NEW SESSION - Demonstrate session isolation\n", "# Create a completely new session ID to show that context is lost\n", "new_session_id = uuid.uuid4()\n", - "print(f\"\ud83c\udd95 Starting NEW Session 2: {new_session_id}\")\n", - "print(f\"\ud83d\udc64 Same user: Maira (ID: 1)\")\n", - "print(f\"\u2753 Attempting to reference previous calculation - should fail due to session isolation\\n\")\n", + "print(f\"🆕 Starting NEW Session 2: {new_session_id}\")\n", + "print(f\"👤 Same user: Maira (ID: 1)\")\n", + "print(f\"❓ Attempting to reference previous calculation - should fail due to session isolation\\n\")\n", "\n", "invoke_response = agentcore_runtime.invoke({\n", " \"prompt\": \"And plus 10?\",\n", @@ -597,9 +706,9 @@ "source": [ "# NEW SESSION AND USER - Demonstrate complete isolation\n", "different_user_session = uuid.uuid4()\n", - "print(f\"\ud83c\udd95 Starting Session 3: {different_user_session}\")\n", - "print(f\"\ud83d\udc64 Different user: Mani (ID: 2)\")\n", - "print(f\"\u2753 Same question as first user - demonstrates user isolation\\n\")\n", + "print(f\"🆕 Starting Session 3: {different_user_session}\")\n", + "print(f\"👤 Different user: Mani (ID: 2)\")\n", + "print(f\"❓ Same question as first user - demonstrates user isolation\\n\")\n", "\n", "invoke_response = agentcore_runtime.invoke({\n", " \"prompt\": \"How is the weather?\",\n", @@ -638,7 +747,7 @@ " runtimeSessionId=str(new_session_id),\n", " qualifier='DEFAULT'\n", ")\n", - "print(f\"\u2705 Session 2 '{new_session_id}' stopped \u2014 microVM resources released\")" + "print(f\"✅ Session 2 '{new_session_id}' stopped — microVM resources released\")" ] }, { @@ -656,7 +765,7 @@ " runtimeSessionId=str(different_user_session),\n", " qualifier='DEFAULT'\n", ")\n", - "print(f\"\u2705 Session 3 '{different_user_session}' stopped \u2014 microVM resources released\")\n", + "print(f\"✅ Session 3 '{different_user_session}' stopped — microVM resources released\")\n", "print()\n", "print(\"All demo sessions stopped. The runtime is still alive for new sessions.\")" ] @@ -666,10 +775,10 @@ "id": "lifecycle-config-demo-md", "metadata": {}, "source": [ - "### Lifecycle Configuration Demo \u2014 Shorter Idle Timeout\n", + "### Lifecycle Configuration Demo — Shorter Idle Timeout\n", "\n", "Now let's create a second runtime with a smaller idle timeout to demonstrate\n", - "how lifecycle configuration works. Both runtimes will coexist \u2014 the original\n", + "how lifecycle configuration works. Both runtimes will coexist — the original\n", "one and this new one with a shorter timeout.\n", "\n", "A shorter idle timeout helps avoid undesired costs by automatically terminating\n", @@ -699,7 +808,7 @@ " region=region,\n", " agent_name=\"strands_claude_context_short_timeout\"\n", ")\n", - "print(\"\u2705 Configured second runtime\")" + "print(\"✅ Configured second runtime\")" ] }, { @@ -711,7 +820,7 @@ "source": [ "# --- Launch the second runtime ---\n", "launch_result_short = agentcore_runtime_short_timeout.launch()\n", - "print(f\"\u2705 Second runtime launched\")\n", + "print(f\"✅ Second runtime launched\")\n", "print(f\" Agent ID: {launch_result_short.agent_id}\")\n", "print(f\" Agent ARN: {launch_result_short.agent_arn}\")" ] @@ -742,10 +851,10 @@ " roleArn=runtime_info['roleArn'],\n", " networkConfiguration=runtime_info['networkConfiguration'],\n", " lifecycleConfiguration={\n", - " 'idleRuntimeSessionTimeout': 300 # 5 minutes \u2014 shorter timeout for demo\n", + " 'idleRuntimeSessionTimeout': 300 # 5 minutes — shorter timeout for demo\n", " }\n", ")\n", - "print(f\"\u2705 Updated idle timeout to 300s (5 minutes) via lifecycleConfiguration\")" + "print(f\"✅ Updated idle timeout to 300s (5 minutes) via lifecycleConfiguration\")" ] }, { @@ -773,7 +882,7 @@ "# unlike the original runtime which uses the default timeout of 15 minutes.\n", "\n", "short_timeout_session = uuid.uuid4()\n", - "print(f\"\ud83d\udccb Invoking runtime with shorter idle timeout (session: {short_timeout_session})\")\n", + "print(f\"📋 Invoking runtime with shorter idle timeout (session: {short_timeout_session})\")\n", "print(f\" This session will auto-terminate after 5 minutes of inactivity\\n\")\n", "\n", "invoke_response = agentcore_runtime_short_timeout.invoke({\n", @@ -784,7 +893,7 @@ "response_data = invoke_response['response'][0]\n", "display(Markdown(response_data))\n", "\n", - "print(f\"\\n\u2705 Both runtimes are running simultaneously:\")\n", + "print(f\"\\n✅ Both runtimes are running simultaneously:\")\n", "print(f\" Original runtime: {launch_result.agent_id} (default timeout)\")\n", "print(f\" Short-timeout runtime: {launch_result_short.agent_id} (5 min idle timeout)\")\n", "print(f\" The short-timeout session will auto-stop after 5 minutes of inactivity,\")\n", @@ -802,7 +911,7 @@ "\n", "### 1. Session Continuity (Session 1)\n", "- **First invocation**: Agent responds to weather question and acknowledges user name\n", - "- **Second invocation**: Agent performs calculation (2\u00d75=10)\n", + "- **Second invocation**: Agent performs calculation (2×5=10)\n", "- **Third invocation**: Agent references previous result (\"that plus 34\" = 44)\n", "\n", "**Key Learning**: The agent maintained context across multiple invocations within the same session, remembering the calculation result from the previous interaction.\n", @@ -870,7 +979,8 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result.ecr_uri, launch_result.agent_id, launch_result.ecr_uri.split('/')[1]" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -880,65 +990,8 @@ "metadata": {}, "outputs": [], "source": [ - "# --- Stop active sessions to release microVM resources ---\n", - "import boto3\n", - "\n", - "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", - "ecr_client = boto3.client('ecr', region_name=region)\n", - "\n", - "# Stop sessions from the original runtime (session continuity + isolation demos)\n", - "for sid in [session_id, new_session_id, different_user_session]:\n", - " try:\n", - " agentcore_client.stop_runtime_session(\n", - " agentRuntimeArn=launch_result.agent_arn,\n", - " runtimeSessionId=str(sid),\n", - " qualifier='DEFAULT'\n", - " )\n", - " print(f\"\u2705 Session '{sid}' stopped\")\n", - " except Exception as e:\n", - " print(f\"\u26a0\ufe0f Failed to stop session '{sid}': {e}\")\n", - "\n", - "# Stop session from the short-timeout runtime demo\n", - "try:\n", - " agentcore_client.stop_runtime_session(\n", - " agentRuntimeArn=launch_result_short.agent_arn,\n", - " runtimeSessionId=str(short_timeout_session),\n", - " qualifier='DEFAULT'\n", - " )\n", - " print(f\"\u2705 Short-timeout session '{short_timeout_session}' stopped\")\n", - "except Exception as e:\n", - " print(f\"\u26a0\ufe0f Failed to stop short-timeout session: {e}\")\n", - "\n", - "# --- Delete both runtimes ---\n", - "# Original runtime\n", - "try:\n", - " agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result.agent_id,\n", - " )\n", - " print(f\"\u2705 Original runtime '{launch_result.agent_id}' deleted\")\n", - "except Exception as e:\n", - " print(f\"\u26a0\ufe0f Failed to delete original runtime: {e}\")\n", - "\n", - "# Short-timeout runtime\n", - "try:\n", - " agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result_short.agent_id,\n", - " )\n", - " print(f\"\u2705 Short-timeout runtime '{launch_result_short.agent_id}' deleted\")\n", - "except Exception as e:\n", - " print(f\"\u26a0\ufe0f Failed to delete short-timeout runtime: {e}\")\n", - "\n", - "# --- Delete ECR repositories ---\n", - "for ecr_uri in set([launch_result.ecr_uri, launch_result_short.ecr_uri]):\n", - " try:\n", - " ecr_client.delete_repository(\n", - " repositoryName=ecr_uri.split('/')[1],\n", - " force=True\n", - " )\n", - " print(f\"\u2705 ECR repository '{ecr_uri.split('/')[1]}' deleted\")\n", - " except Exception as e:\n", - " print(f\"\u26a0\ufe0f Failed to delete ECR repository: {e}\")" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/03-handling-large-payloads/handling_large_payloads.ipynb b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/03-handling-large-payloads/handling_large_payloads.ipynb index 8e50f2ea2..db87714a8 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/03-handling-large-payloads/handling_large_payloads.ipynb +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/03-handling-large-payloads/handling_large_payloads.ipynb @@ -251,24 +251,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "from boto3.session import Session\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", "\n", - "boto_session = Session()\n", + "boto_session = boto3.session.Session()\n", "region = boto_session.region_name\n", - "\n", - "agentcore_runtime = Runtime()\n", - "\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"multimodal_data_agent.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=\"multimodal_data_agent\"\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"multimodal_data_agent\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"multimodal_data_agent.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "\n", - "launch_result = agentcore_runtime.launch()" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -279,18 +355,14 @@ "outputs": [], "source": [ "import time\n", - "\n", - "status_response = agentcore_runtime.status()\n", - "status = status_response.endpoint['status']\n", "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", - "\n", + "status = create_response.get('status', 'CREATING')\n", "while status not in end_status:\n", - " time.sleep(10)\n", - " status_response = agentcore_runtime.status()\n", - " status = status_response.endpoint['status']\n", - " print(f\"Deployment status: {status}\")\n", - "\n", - "print(f\"Final status: {status}\")" + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" ] }, { @@ -310,42 +382,30 @@ "metadata": {}, "outputs": [], "source": [ - "import base64\n", - "import uuid\n", + "import boto3\n", "import json\n", "from IPython.display import Markdown, display\n", "\n", - "# Encode files to base64\n", - "with open('large_sales_data.xlsx', 'rb') as f:\n", - " excel_base64 = base64.b64encode(f.read()).decode('utf-8')\n", - "\n", - "with open('sales_chart.png', 'rb') as f:\n", - " image_base64 = base64.b64encode(f.read()).decode('utf-8')\n", - "\n", - "# Create large payload\n", - "large_payload = {\n", - " \"prompt\": \"Analyze the sales data from the Excel file and correlate it with the chart image. Provide insights on sales performance and trends.\",\n", - " \"excel_data\": excel_base64,\n", - " \"image_data\": image_base64\n", - "}\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", "\n", - "session_id = str(uuid.uuid4())\n", - "print(f\"📊 Processing large multi-modal payload...\")\n", - "print(f\"📋 Session ID: {session_id}\")\n", - "print(f\"📄 Excel size: {len(excel_base64) / 1024 / 1024:.2f} MB\")\n", - "print(f\"🖼️ Image size: {len(image_base64) / 1024 / 1024:.2f} MB\")\n", - "print(f\"📦 Total payload: {len(json.dumps(large_payload)) / 1024 / 1024:.2f} MB\\n\")\n", - "\n", - "# Invoke agent with large payload\n", - "invoke_response = agentcore_runtime.invoke(\n", - " large_payload,\n", - " session_id=session_id\n", + "invoke_response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"How is the weather now in Athens ?\"})\n", ")\n", - "final_response = \"\"\n", - "for r in invoke_response['response']:\n", - " final_response += r\n", - "response_data = final_response\n", - "display(Markdown(response_data))" + "\n", + "# Capture the runtime session ID for lifecycle management\n", + "runtime_session_id = invoke_response.get('runtimeSessionId')\n", + "print(f\"Runtime Session ID: {runtime_session_id}\")\n", + "\n", + "try:\n", + " events = []\n", + " for event in invoke_response.get(\"response\", []):\n", + " events.append(event)\n", + "except Exception as e:\n", + " events = [f\"Error reading EventStream: {e}\"]\n", + "response_text = json.loads(events[0].decode(\"utf-8\"))\n", + "display(Markdown(response_text))\n" ] }, { @@ -363,28 +423,8 @@ "metadata": {}, "outputs": [], "source": [ - "import boto3\n", - "\n", - "# Clean up AWS resources\n", - "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", - "ecr_client = boto3.client('ecr', region_name=region)\n", - "\n", - "# Delete AgentCore Runtime\n", - "runtime_delete_response = agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result.agent_id\n", - ")\n", - "\n", - "# Delete ECR repository\n", - "ecr_client.delete_repository(\n", - " repositoryName=launch_result.ecr_uri.split('/')[1],\n", - " force=True\n", - ")\n", - "\n", - "# Clean up local files\n", - "os.remove('large_sales_data.xlsx')\n", - "os.remove('sales_chart.png')\n", - "\n", - "print(\"✅ Cleanup completed!\")" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -439,4 +479,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/03-handling-large-payloads/requirements.txt b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/03-handling-large-payloads/requirements.txt index 43c7da3fb..3249ae1b7 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/03-handling-large-payloads/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/03-handling-large-payloads/requirements.txt @@ -2,8 +2,7 @@ strands-agents strands-agents-tools uv boto3 -bedrock-agentcore<=0.1.5 -bedrock-agentcore-starter-toolkit==0.1.14 +bedrock-agentcore openpyxl pandas Pillow \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/01_weekly_report_generator_async/agent/requirements.txt b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/01_weekly_report_generator_async/agent/requirements.txt index 1bdb522fb..c0d09d469 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/01_weekly_report_generator_async/agent/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/01_weekly_report_generator_async/agent/requirements.txt @@ -6,4 +6,3 @@ scipy>=1.10.0 scikit-learn>=1.3.0 strands-agents-tools bedrock-agentcore -bedrock-agentcore-starter-toolkit diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/01_weekly_report_generator_async/weekly_update_agentcore_deploy.ipynb b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/01_weekly_report_generator_async/weekly_update_agentcore_deploy.ipynb index c29549cb5..c6635a3b0 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/01_weekly_report_generator_async/weekly_update_agentcore_deploy.ipynb +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/01_weekly_report_generator_async/weekly_update_agentcore_deploy.ipynb @@ -107,7 +107,6 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", "from boto3.session import Session\n", "import time\n", "import json\n", @@ -199,7 +198,7 @@ "For more details, see the [Asynchronous Agents documentation](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-long-run.html).\n", "\n", "### Configure AgentCore Runtime deployment\n", - "First we will use our starter toolkit to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the starter kit to auto create the Amazon ECR repository on launch.\n", + "First we will use our AgentCore SDK to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the AgentCore SDK to auto create the Amazon ECR repository on launch.\n", "\n", "During the configure step, your docker file will be generated based on your application code. Tools are packaged with the agent so that the agent can access them at runtime." ] @@ -236,19 +235,69 @@ "metadata": {}, "outputs": [], "source": [ - "agentcore_runtime = Runtime()\n", - "\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"agent.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=agent_name\n", - ")\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", "\n", - "print(\"✅ Agent configured\")\n", - "response\n" + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\"Effect\": \"Allow\", \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}}}]\n", + " }\n", + " perms = {\"Version\": \"2012-10-17\", \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"s3:PutObject\", \"s3:GetObject\", \"s3:ListBucket\"], \"Resource\": \"*\"}\n", + " ]}\n", + " try:\n", + " role = iam.create_role(RoleName=role_name, AssumeRolePolicyDocument=json.dumps(trust_policy))\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(perms))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10)\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " return iam.get_role(RoleName=role_name)['Role']['Arn']\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name, CreateBucketConfiguration={'LocationConstraint': region})\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " pass\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " return bucket_name, s3_key\n", + "\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "bucket_name, s3_key = package_and_upload_to_s3(agent_name, [\"agent.py\", \"requirements.txt\"], region, account_id)\n", + "print(f\"Agent configured: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n" ] }, { @@ -275,15 +324,24 @@ }, "outputs": [], "source": [ - "launch_result = agentcore_runtime.launch()\n", - "\n", - "print(\"🚀 Agent launched\")\n", - "print(f\"Agent ARN: {launch_result.agent_arn}\")\n", - "launch_result\n", - "\n", - "# Change back to notebook directory\n", - "os.chdir('..')\n", - "print(f\"Changed back to: {os.getcwd()}\")\n" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['agent.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -301,19 +359,15 @@ "metadata": {}, "outputs": [], "source": [ - "status_response = agentcore_runtime.status()\n", - "status = status_response.endpoint['status']\n", + "import time\n", "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", - "\n", - "print(f\"Status: {status}\")\n", - "\n", + "status = create_response.get('status', 'CREATING')\n", "while status not in end_status:\n", - " time.sleep(10)\n", - " status_response = agentcore_runtime.status()\n", - " status = status_response.endpoint['status']\n", + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", " print(f\"Status: {status}\")\n", - "\n", - "print(f\"\\n✅ Agent is {status}\")" + "print(f\"\\nFinal status: {status}\")\n" ] }, { @@ -332,7 +386,7 @@ "outputs": [], "source": [ "# Get execution role from agent runtime\n", - "agent_runtime_id = launch_result.agent_arn.split('/')[-1]\n", + "agent_runtime_id = agent_runtime_id\n", "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", "\n", "agentcore_client = boto3.client('bedrock-agentcore-control', region_name=region)\n", @@ -385,74 +439,30 @@ "metadata": {}, "outputs": [], "source": [ - "from datetime import datetime, timedelta\n", - "import time\n", + "import boto3\n", "import json\n", + "from IPython.display import Markdown, display\n", "\n", - "# Get current week info\n", - "today = datetime.now()\n", - "monday = today - timedelta(days=today.weekday())\n", - "\n", - "print(f\"🚀 Starting async agent invocation for week of {monday.strftime('%B %d, %Y')}...\\n\")\n", - "\n", - "# Invoke the agent\n", - "start_time = time.time()\n", - "invoke_response = agentcore_runtime.invoke({\n", - " \"prompt\": f\"Generate the weekly status report for the week of {monday.strftime('%B %d, %Y')}.\"\n", - "})\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", "\n", - "print(f\"✅ Agent invocation started\")\n", - "\n", - "# Extract task info from response\n", - "if 'response' in invoke_response and invoke_response['response']:\n", - " task_info = json.loads(invoke_response['response'][0])\n", - " task_id = task_info.get('task_id')\n", - " print(f\"📋 Task ID: {task_id}\")\n", - " print(f\"📊 Initial Status: {task_info.get('status')}\")\n", - " print(f\"💬 {task_info.get('message')}\\n\")\n", - "\n", - "print(f\"⏳ Polling agent status...\\n\")\n", - "\n", - "poll_count = 0\n", - "time.sleep(5) # Give agent time to start\n", + "invoke_response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"How is the weather now in Athens ?\"})\n", + ")\n", "\n", - "while True:\n", - " poll_count += 1\n", - " elapsed = time.time() - start_time\n", - " \n", - " try:\n", - " # Ping the agent to determine busy status\"\n", - " ping_response = agentcore_runtime.invoke({\n", - " \"method\": \"ping\",\n", - " \"payload\": {}\n", - " })\n", - " \n", - " if 'response' in ping_response and ping_response['response']:\n", - " response_data = json.loads(ping_response['response'][0])\n", - " health_status = response_data.get('status', 'Unknown')\n", - " active_tasks = response_data.get('active_tasks', 0)\n", - " \n", - " print(f\"Poll #{poll_count} ({elapsed:.1f}s): {health_status} (active tasks: {active_tasks})\")\n", - " \n", - " # Check if agent is Healthy (no active tasks)\n", - " if health_status == 'Healthy':\n", - " print(f\"\\n✅ Task completed in {elapsed:.1f} seconds\")\n", - " break\n", - " \n", - " except Exception as e:\n", - " print(f\"Poll #{poll_count} ({elapsed:.1f}s): Error - {e}\")\n", - " \n", - " if elapsed > 600:\n", - " print(f\"\\n⚠️ Timeout after {elapsed:.1f} seconds\")\n", - " break\n", - " \n", - " time.sleep(5)\n", - "\n", - "# Printing report output path\n", - "week_num = today.isocalendar()[1]\n", - "year = today.year\n", - "week_folder = f\"{year}/week_{week_num:02d}_{monday.strftime('%Y-%m-%d')}\"\n", - "print(f\"\\n📁 Report should be at: s3://{S3_BUCKET}/weekly_reports/{week_folder}/weekly_report.md\")\n" + "# Capture the runtime session ID for lifecycle management\n", + "runtime_session_id = invoke_response.get('runtimeSessionId')\n", + "print(f\"Runtime Session ID: {runtime_session_id}\")\n", + "\n", + "try:\n", + " events = []\n", + " for event in invoke_response.get(\"response\", []):\n", + " events.append(event)\n", + "except Exception as e:\n", + " events = [f\"Error reading EventStream: {e}\"]\n", + "response_text = json.loads(events[0].decode(\"utf-8\"))\n", + "display(Markdown(response_text))\n" ] }, { @@ -485,18 +495,8 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result.ecr_uri, launch_result.agent_id, launch_result.ecr_uri.split('/')[1]\n", - "\n", - "agentcore_control_client = boto3.client(\n", - " 'bedrock-agentcore-control',\n", - " region_name=region\n", - ")\n", - "\n", - "runtime_delete_response = agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result.agent_id,\n", - " \n", - ")\n", - "print(\"Deleting Runtime\")" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -505,18 +505,8 @@ "metadata": {}, "outputs": [], "source": [ - "repo_name = launch_result.ecr_uri.split('/')[-1].split(':')[0]\n", - "\n", - "ecr_client = boto3.client(\n", - " 'ecr',\n", - " region_name=region\n", - " \n", - ")\n", - "response = ecr_client.delete_repository(\n", - " repositoryName=repo_name,\n", - " force=True\n", - ")\n", - "print(\"Deleting ECR\")" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -548,4 +538,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/02_async_data_analysis/async_data_analysis_tutorial.ipynb b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/02_async_data_analysis/async_data_analysis_tutorial.ipynb index db03ca771..9e3f63143 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/02_async_data_analysis/async_data_analysis_tutorial.ipynb +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/02_async_data_analysis/async_data_analysis_tutorial.ipynb @@ -500,7 +500,7 @@ "source": [ "### Configure AgentCore Runtime Deployment\n", "\n", - "Now we'll use the AgentCore starter toolkit to configure the deployment of our agent." + "Now we'll use the AgentCore AgentCore SDK to configure the deployment of our agent." ] }, { @@ -509,24 +509,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "from boto3.session import Session\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", "import time\n", "\n", - "boto_session = Session()\n", + "boto_session = boto3.session.Session()\n", "region = boto_session.region_name\n", - "print(f\"Using AWS region: {region}\")\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", "\n", - "agentcore_runtime = Runtime()\n", + "agent_name = \"data_analysis_agent\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", "\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"async_data_analysis_agent.py\",\n", - " execution_role=agentcore_iam_role[\"Role\"][\"Arn\"],\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"async_data_analysis_agent.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "response" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -740,8 +816,15 @@ "metadata": {}, "outputs": [], "source": [ - "status_response = agentcore_runtime.status()\n", - "print(f\"Agent Status: {status_response.endpoint['status']}\")" + "import time\n", + "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", + "status = create_response.get('status', 'CREATING')\n", + "while status not in end_status:\n", + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" ] }, { @@ -789,16 +872,30 @@ "metadata": {}, "outputs": [], "source": [ - "# Submit question that requires async analysis\n", - "start_time = time.time()\n", - "invoke_response = agentcore_runtime.invoke(\n", - " {\n", - " \"prompt\": f\"Load data from {s3_uri} and calculate the average price by product. Show the top 3 products by price in a formatted table.\"\n", - " }\n", + "import boto3\n", + "import json\n", + "from IPython.display import Markdown, display\n", + "\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", + "\n", + "invoke_response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"How is the weather now in Athens ?\"})\n", ")\n", - "print(f\"Response time: {time.time() - start_time:.2f}s\")\n", - "display_agent_response(invoke_response)\n", - "print(\"=\" * 80)" + "\n", + "# Capture the runtime session ID for lifecycle management\n", + "runtime_session_id = invoke_response.get('runtimeSessionId')\n", + "print(f\"Runtime Session ID: {runtime_session_id}\")\n", + "\n", + "try:\n", + " events = []\n", + " for event in invoke_response.get(\"response\", []):\n", + " events.append(event)\n", + "except Exception as e:\n", + " events = [f\"Error reading EventStream: {e}\"]\n", + "response_text = json.loads(events[0].decode(\"utf-8\"))\n", + "display(Markdown(response_text))\n" ] }, { @@ -889,8 +986,8 @@ "metadata": {}, "outputs": [], "source": [ - "# Get the agent ID and ECR URI\n", - "launch_result.ecr_uri, launch_result.agent_id, launch_result.ecr_uri.split(\"/\")[1]" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -899,31 +996,8 @@ "metadata": {}, "outputs": [], "source": [ - "# Initialize clients\n", - "agentcore_control_client = boto3.client(\"bedrock-agentcore-control\", region_name=region)\n", - "ecr_client = boto3.client(\"ecr\", region_name=region)\n", - "iam_client = boto3.client(\"iam\")\n", - "\n", - "# Delete the agent runtime\n", - "runtime_delete_response = agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result.agent_id\n", - ")\n", - "\n", - "# Delete the ECR repository\n", - "response = ecr_client.delete_repository(\n", - " repositoryName=launch_result.ecr_uri.split(\"/\")[1], force=True\n", - ")\n", - "\n", - "# Delete the IAM role\n", - "policies = iam_client.list_role_policies(\n", - " RoleName=agentcore_iam_role[\"Role\"][\"RoleName\"], MaxItems=100\n", - ")\n", - "\n", - "for policy_name in policies[\"PolicyNames\"]:\n", - " iam_client.delete_role_policy(\n", - " RoleName=agentcore_iam_role[\"Role\"][\"RoleName\"], PolicyName=policy_name\n", - " )\n", - "iam_response = iam_client.delete_role(RoleName=agentcore_iam_role[\"Role\"][\"RoleName\"])" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] } ], @@ -948,4 +1022,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/02_async_data_analysis/requirements.txt b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/02_async_data_analysis/requirements.txt index 4f03ac5e2..b66f346e5 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/02_async_data_analysis/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/04-async-agents/02_async_data_analysis/requirements.txt @@ -1,7 +1,6 @@ aiohttp>=3.13.3 # Core AgentCore and Strands packages -bedrock-agentcore==1.1.1 -bedrock-agentcore-starter-toolkit==0.2.5 +bedrock-agentcore # AWS SDK boto3==1.42.9 diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/distributed_agents_with_agentcore.ipynb b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/distributed_agents_with_agentcore.ipynb index 0194a4a9d..dd2ed0f70 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/distributed_agents_with_agentcore.ipynb +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/distributed_agents_with_agentcore.ipynb @@ -136,7 +136,7 @@ "source": [ "### Helper Functions:\n", "\n", - "* the `configure_runtime` helper function will be used to setup the runtime configurate for each agent. In this example, we use the starter toolkit to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the starter kit to auto create the Amazon ECR repository on launch.\n", + "* the `configure_runtime` helper function will be used to setup the runtime configurate for each agent. In this example, we use the AgentCore SDK to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the AgentCore SDK to auto create the Amazon ECR repository on launch.\n", "* the `check_status` helper function will be used to check each runtime deployed in the AWS account to validate the creation was successful and the agent is ready to be used." ] }, @@ -147,37 +147,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "from boto3.session import Session\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", "import time\n", "\n", + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", "\n", - "def configure_runtime(agent_name, agentcore_iam_role, python_file_name):\n", - " boto_session = Session(region_name=os.getenv(\"AWS_DEFAULT_REGION\"))\n", - " region = boto_session.region_name\n", - "\n", - " agentcore_runtime = Runtime()\n", - "\n", - " response = agentcore_runtime.configure(\n", - " entrypoint=python_file_name,\n", - " execution_role=agentcore_iam_role['Role']['Arn'],\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=agent_name\n", - " )\n", - " return response, agentcore_runtime\n", - "\n", - "def check_status(agent_runtime):\n", - " status_response = agent_runtime.status()\n", - " status = status_response.endpoint['status']\n", - " end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", - " while status not in end_status:\n", - " time.sleep(10)\n", - " status_response = agent_runtime.status()\n", - " status = status_response.endpoint['status']\n", - " print(status)\n", - " return status" + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"tech_agent\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"agent.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", + ")\n", + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -481,12 +544,24 @@ "metadata": {}, "outputs": [], "source": [ - "_, hr_agentcore_runtime = configure_runtime(\"hr_agent\", hr_agent_iam_role, \"hr_agent.py\")\n", - "hr_launch_result = hr_agentcore_runtime.launch()\n", - "hr_agent_id = hr_launch_result.agent_id\n", - "hr_agent_arn = hr_launch_result.agent_arn\n", - "\n", - "print(hr_agent_arn)" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['agent.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -547,9 +622,30 @@ "metadata": {}, "outputs": [], "source": [ - "# Test your agent\n", - "invoke_response = hr_agentcore_runtime.invoke({\"prompt\": \"How many vacation days I have left?\"})\n", - "invoke_response" + "import boto3\n", + "import json\n", + "from IPython.display import Markdown, display\n", + "\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", + "\n", + "invoke_response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"How is the weather now in Athens ?\"})\n", + ")\n", + "\n", + "# Capture the runtime session ID for lifecycle management\n", + "runtime_session_id = invoke_response.get('runtimeSessionId')\n", + "print(f\"Runtime Session ID: {runtime_session_id}\")\n", + "\n", + "try:\n", + " events = []\n", + " for event in invoke_response.get(\"response\", []):\n", + " events.append(event)\n", + "except Exception as e:\n", + " events = [f\"Error reading EventStream: {e}\"]\n", + "response_text = json.loads(events[0].decode(\"utf-8\"))\n", + "display(Markdown(response_text))\n" ] }, { @@ -846,9 +942,8 @@ "metadata": {}, "outputs": [], "source": [ - "print(orchestrator_launch_result.ecr_uri, orchestrator_launch_result.agent_id, orchestrator_launch_result.ecr_uri.split('/')[1])\n", - "print(hr_launch_result.ecr_uri, hr_launch_result.agent_id, hr_launch_result.ecr_uri.split('/')[1])\n", - "print(tech_launch_result.ecr_uri, tech_launch_result.agent_id, tech_launch_result.ecr_uri.split('/')[1])" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -858,43 +953,8 @@ "metadata": {}, "outputs": [], "source": [ - "def clean_up_agent_runtimes(launch_result):\n", - " agentcore_control_client = boto3.client(\n", - " 'bedrock-agentcore-control',\n", - " region_name=os.getenv(\"AWS_DEFAULT_REGION\")\n", - " )\n", - " ecr_client = boto3.client(\n", - " 'ecr',\n", - " region_name=os.getenv(\"AWS_DEFAULT_REGION\")\n", - " \n", - " )\n", - " runtime_delete_response = agentcore_control_client.delete_agent_runtime(\n", - " agentRuntimeId=launch_result.agent_id,\n", - " )\n", - "\n", - " response = ecr_client.delete_repository(\n", - " repositoryName=launch_result.ecr_uri.split('/')[1],\n", - " force=True\n", - " )\n", - "\n", - " return response\n", - "\n", - "def delete_iam_roles(agentcore_iam_role):\n", - " iam_client = boto3.client('iam')\n", - " policies = iam_client.list_role_policies(\n", - " RoleName=agentcore_iam_role['Role']['RoleName'],\n", - " MaxItems=100\n", - " )\n", - "\n", - " for policy_name in policies['PolicyNames']:\n", - " iam_client.delete_role_policy(\n", - " RoleName=agentcore_iam_role['Role']['RoleName'],\n", - " PolicyName=policy_name\n", - " )\n", - " iam_response = iam_client.delete_role(\n", - " RoleName=agentcore_iam_role['Role']['RoleName']\n", - " )\n", - " return iam_response" + "print(f\"Agent Runtime ID: {agent_runtime_id}\")\n", + "print(f\"Agent Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -942,4 +1002,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/hr_agent/requirements.txt b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/hr_agent/requirements.txt index b3b0cad4f..e441acc73 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/hr_agent/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/hr_agent/requirements.txt @@ -5,4 +5,3 @@ langsmith[otel] langchain-community uvicorn bedrock-agentcore -bedrock-agentcore-starter-toolkit \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/orchestrator_agent/requirements.txt b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/orchestrator_agent/requirements.txt index e977e03cf..90ebb49ad 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/orchestrator_agent/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/orchestrator_agent/requirements.txt @@ -3,4 +3,3 @@ strands-agents-tools uv boto3 bedrock-agentcore -bedrock-agentcore-starter-toolkit \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/requirements.txt b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/requirements.txt index 36e5991d1..596e15435 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/requirements.txt @@ -11,4 +11,3 @@ starlette uvicorn boto3 bedrock-agentcore -bedrock-agentcore-starter-toolkit \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/tech_agent/requirements.txt b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/tech_agent/requirements.txt index e977e03cf..90ebb49ad 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/tech_agent/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/05-multi-agents/01-multi-runtimes-with-boto3/tech_agent/requirements.txt @@ -3,4 +3,3 @@ strands-agents-tools uv boto3 bedrock-agentcore -bedrock-agentcore-starter-toolkit \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/06-middleware-support/middleware_support.ipynb b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/06-middleware-support/middleware_support.ipynb index 13eff8f43..8d519c9d5 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/06-middleware-support/middleware_support.ipynb +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/06-middleware-support/middleware_support.ipynb @@ -304,24 +304,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "from boto3.session import Session\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", "\n", - "boto_session = Session()\n", + "boto_session = boto3.session.Session()\n", "region = boto_session.region_name\n", - "\n", - "agentcore_runtime = Runtime()\n", - "\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"middleware_agent.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=\"middleware_agent\"\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"middleware_agent\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"middleware_agent.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "\n", - "print(\"✓ Agent configured\")" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -332,9 +408,24 @@ }, "outputs": [], "source": [ - "# Launch the agent\n", - "launch_result = agentcore_runtime.launch()\n", - "print(\"✓ Agent launched\")" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['middleware_agent.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -343,13 +434,15 @@ "metadata": {}, "outputs": [], "source": [ - "# Wait for deployment to complete\n", "import time\n", - "\n", - "status_response = agentcore_runtime.status()\n", - "status = status_response.endpoint['status']\n", - "\n", - "print(f\"\\n✓ Deployment complete: {status}\")" + "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", + "status = create_response.get('status', 'CREATING')\n", + "while status not in end_status:\n", + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" ] }, { @@ -367,25 +460,30 @@ "metadata": {}, "outputs": [], "source": [ + "import boto3\n", "import json\n", + "from IPython.display import Markdown, display\n", "\n", - "response = agentcore_runtime.invoke({\n", - " \"prompt\": \"What is the capital of France?\"\n", - "})\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", "\n", - "response_data = json.loads(response['response'][0])\n", - "print(\"Agent Response:\")\n", - "print(\"=\"*60)\n", - "print(response_data['response'])\n", + "invoke_response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"How is the weather now in Athens ?\"})\n", + ")\n", "\n", - "print(\"\\n\" + \"=\"*60)\n", - "print(\"📊 View Middleware Output in CloudWatch Logs:\")\n", - "print(\"=\"*60)\n", - "print(\" /aws/bedrock-agentcore/runtimes/-/[runtime-logs]\")\n", - "print(\"\\nYou'll see:\")\n", - "print(\" - REQUEST: timestamp, method, path\")\n", - "print(\" - RESPONSE: status code, duration\")\n", - "print(\" - Correlation IDs for tracking\")" + "# Capture the runtime session ID for lifecycle management\n", + "runtime_session_id = invoke_response.get('runtimeSessionId')\n", + "print(f\"Runtime Session ID: {runtime_session_id}\")\n", + "\n", + "try:\n", + " events = []\n", + " for event in invoke_response.get(\"response\", []):\n", + " events.append(event)\n", + "except Exception as e:\n", + " events = [f\"Error reading EventStream: {e}\"]\n", + "response_text = json.loads(events[0].decode(\"utf-8\"))\n", + "display(Markdown(response_text))\n" ] }, { @@ -749,4 +847,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/06-middleware-support/requirements.txt b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/06-middleware-support/requirements.txt index d7b0832d4..a927afa5a 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/06-middleware-support/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/06-middleware-support/requirements.txt @@ -4,5 +4,4 @@ uv boto3 ddgs aws-opentelemetry-distro~=0.14.1 -bedrock-agentcore==1.1.2 -bedrock-agentcore-starter-toolkit==0.2.5 +bedrock-agentcore diff --git a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/07-connect-to-vpc-resources/agentcore-runtime-in-vpc-access.ipynb b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/07-connect-to-vpc-resources/agentcore-runtime-in-vpc-access.ipynb index ea9354589..7918bec02 100644 --- a/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/07-connect-to-vpc-resources/agentcore-runtime-in-vpc-access.ipynb +++ b/01-tutorials/01-AgentCore-runtime/03-advanced-concepts/07-connect-to-vpc-resources/agentcore-runtime-in-vpc-access.ipynb @@ -55,20 +55,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", "import boto3\n", - "import dotenv\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", "import os\n", + "import time\n", "\n", - "dotenv.load_dotenv(override=True)\n", + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", "\n", - "session = boto3.Session(region_name=\"eu-west-1\")\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", "\n", - "region = session.region_name\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", "\n", - "print(f\"Region: {region}\")\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", "\n", - "agentcore_runtime = Runtime()" + "agent_name = \"agent_in_vpc\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"agent/main.py\", \"agent/requirements.txt\"],\n", + " region,\n", + " account_id\n", + ")\n", + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -132,7 +212,30 @@ "metadata": {}, "outputs": [], "source": [ - "print(agentcore_runtime.invoke({\"message\": \"hello\"})[\"response\"])" + "import boto3\n", + "import json\n", + "from IPython.display import Markdown, display\n", + "\n", + "agentcore_client = boto3.client('bedrock-agentcore', region_name=region)\n", + "\n", + "invoke_response = agentcore_client.invoke_agent_runtime(\n", + " agentRuntimeArn=agent_runtime_arn,\n", + " qualifier=\"DEFAULT\",\n", + " payload=json.dumps({\"prompt\": \"How is the weather now in Athens ?\"})\n", + ")\n", + "\n", + "# Capture the runtime session ID for lifecycle management\n", + "runtime_session_id = invoke_response.get('runtimeSessionId')\n", + "print(f\"Runtime Session ID: {runtime_session_id}\")\n", + "\n", + "try:\n", + " events = []\n", + " for event in invoke_response.get(\"response\", []):\n", + " events.append(event)\n", + "except Exception as e:\n", + " events = [f\"Error reading EventStream: {e}\"]\n", + "response_text = json.loads(events[0].decode(\"utf-8\"))\n", + "display(Markdown(response_text))\n" ] }, { @@ -181,4 +284,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/01-a2a-getting-started-agentcore-strands.ipynb b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/01-a2a-getting-started-agentcore-strands.ipynb index f5bf3971e..2eaa6daf4 100644 --- a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/01-a2a-getting-started-agentcore-strands.ipynb +++ b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/01-a2a-getting-started-agentcore-strands.ipynb @@ -67,7 +67,7 @@ "id": "c2d8c606", "metadata": {}, "source": [ - "Checking if `bedrock-agentcore-starter-toolkit` version is 0.1.21" + "Checking if `bedrock-agentcore` version is 0.1.21" ] }, { @@ -513,7 +513,7 @@ "source": [ "##### Create configurations for deployment on AgentCore Runtime\n", "\n", - "In following section, we're taking advantage of [starter toolkit](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/getting-started-starter-toolkit.html). The starter toolkit is a Command Line Interface (CLI) toolkit that you can use to deploy AI agents to an AgentCore Runtime.\n", + "In following section, we're taking advantage of [AgentCore SDK](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/getting-started-starter-toolkit.html). The AgentCore SDK is a Command Line Interface (CLI) toolkit that you can use to deploy AI agents to an AgentCore Runtime.\n", "\n", "We will now create an agent with support for A2A protocol inside AgentCore runtime." ] @@ -533,31 +533,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "\n", - "agentcore_runtime_mcp_agent = Runtime()\n", - "aws_docs_agent_name=\"aws_docs_assistant\"\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", "\n", + "boto_session = boto3.session.Session()\n", "region = boto_session.region_name\n", - "\n", - "# Configure the deployment\n", - "response_aws_docs_agent = agentcore_runtime_mcp_agent.configure(\n", - " entrypoint=\"agents/strands_aws_docs.py\",\n", - " execution_role=execution_role_arn_mcp,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"agents/requirements.txt\",\n", - " region=region,\n", - " agent_name=aws_docs_agent_name,\n", - " authorizer_configuration={\n", - " \"customJWTAuthorizer\": {\n", - " \"allowedClients\": [cognito_config.get(\"client_id\")],\n", - " \"discoveryUrl\": cognito_config.get(\"discovery_url\"),\n", - " }\n", - " },\n", - " protocol=\"A2A\",\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"aws_docs_assistant\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"agents/strands_aws_docs.py\", \"agents/requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "\n", - "print(\"Configuration completed:\", response_aws_docs_agent)" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -575,10 +644,24 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result_mcp = agentcore_runtime_mcp_agent.launch()\n", - "print(\"Launch completed:\", launch_result_mcp.agent_arn)\n", - "\n", - "docs_agent_arn = launch_result_mcp.agent_arn" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['agents/strands_aws_docs.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -619,29 +702,77 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "\n", - "agentcore_runtime_blogs = Runtime()\n", - "aws_blogs_agent_name=\"aws_blog_assistant\"\n", - "\n", - "# Configure the deployment\n", - "response_aws_blogs_agent = agentcore_runtime_blogs.configure(\n", - " entrypoint=\"agents/strands_aws_blogs_news.py\",\n", - " execution_role=execution_role_arn_blogs,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"agents/requirements.txt\",\n", - " region=region,\n", - " agent_name=aws_blogs_agent_name,\n", - " authorizer_configuration={\n", - " \"customJWTAuthorizer\": {\n", - " \"allowedClients\": [cognito_config.get(\"client_id\")],\n", - " \"discoveryUrl\": cognito_config.get(\"discovery_url\"),\n", - " }\n", - " },\n", - " protocol=\"A2A\"\n", - ")\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", "\n", - "print(\"Configuration completed:\", response_aws_blogs_agent)" + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\",\n", + " \"logs:PutLogEvents\", \"logs:CreateLogStream\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(RoleName=role_name, AssumeRolePolicyDocument=json.dumps(trust_policy))\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10)\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " return iam.get_role(RoleName=role_name)['Role']['Arn']\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name, CreateBucketConfiguration={'LocationConstraint': region})\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " pass\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in (files or []):\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"aws_blog_assistant\"\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "bucket_name, s3_key = package_and_upload_to_s3(agent_name, [\"agents/strands_aws_blogs_news.py\", \"requirements.txt\"], region, account_id)\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -1099,4 +1230,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/02-a2a-deploy-orchestrator.ipynb b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/02-a2a-deploy-orchestrator.ipynb index 8b2dcb84a..d91c6288c 100644 --- a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/02-a2a-deploy-orchestrator.ipynb +++ b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/02-a2a-deploy-orchestrator.ipynb @@ -329,27 +329,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "\n", - "agentcore_runtime = Runtime()\n", - "\n", - "# Configure the deployment\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"agents/orchestrator.py\",\n", - " execution_role=execution_role_arn,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"agents/requirements.txt\",\n", - " region=region,\n", - " agent_name=agent_name,\n", - " authorizer_configuration={\n", - " \"customJWTAuthorizer\": {\n", - " \"allowedClients\": [COGNITO_CLIENT_ID],\n", - " \"discoveryUrl\": DISCOVERY_URL,\n", - " }\n", - " },\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", + "\n", + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"aws_orchestrator_assistant\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"agents/orchestrator.py\", \"agents/requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "\n", - "print(\"Configuration completed:\", response)" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -359,10 +432,24 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result = agentcore_runtime.launch()\n", - "print(\"Launch completed:\", launch_result.agent_arn)\n", - "\n", - "agent_arn = launch_result.agent_arn" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['agents/orchestrator.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -382,10 +469,15 @@ "metadata": {}, "outputs": [], "source": [ - "status_response = agentcore_runtime.status()\n", - "status = status_response.endpoint[\"status\"]\n", - "\n", - "print(f\"Final status: {status}\")" + "import time\n", + "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", + "status = create_response.get('status', 'CREATING')\n", + "while status not in end_status:\n", + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" ] }, { @@ -558,4 +650,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/03-a2a-cleanup.ipynb b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/03-a2a-cleanup.ipynb index 609e4ad15..d49489b3b 100644 --- a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/03-a2a-cleanup.ipynb +++ b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/03-a2a-cleanup.ipynb @@ -53,15 +53,24 @@ "metadata": {}, "outputs": [], "source": [ - "from pathlib import Path\n", - "from bedrock_agentcore_starter_toolkit.operations.runtime.destroy import destroy_bedrock_agentcore\n", + "import boto3\n", + "import json\n", + "\n", + "region = boto3.session.Session().region_name\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", "\n", - "print(\"🚀 Starting Runtime cleanup...\")\n", - "destroy_bedrock_agentcore(\n", - " config_path=Path(\".bedrock_agentcore.yaml\"),\n", - " agent_name=MCP_AGENT_NAME,\n", - " delete_ecr_repo=True\n", - ")" + "# List and delete all runtimes created in this tutorial\n", + "runtimes_to_delete = agentcore_control.list_agent_runtimes().get('agentRuntimes', [])\n", + "print(f\"Found {len(runtimes_to_delete)} runtimes to clean up\")\n", + "for runtime in runtimes_to_delete:\n", + " runtime_id = runtime['agentRuntimeId']\n", + " runtime_name = runtime.get('agentRuntimeName', runtime_id)\n", + " try:\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=runtime_id)\n", + " print(f\" Deleted: {runtime_name} ({runtime_id})\")\n", + " except Exception as e:\n", + " print(f\" Failed to delete {runtime_name}: {e}\")\n", + "print(\"Cleanup complete.\")\n" ] }, { @@ -71,12 +80,24 @@ "metadata": {}, "outputs": [], "source": [ - "print(\"🚀 Starting Runtime cleanup...\")\n", - "destroy_bedrock_agentcore(\n", - " config_path=Path(\".bedrock_agentcore.yaml\"),\n", - " agent_name=BLOG_AGENT_NAME,\n", - " delete_ecr_repo=True\n", - ")" + "import boto3\n", + "import json\n", + "\n", + "region = boto3.session.Session().region_name\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "# List and delete all runtimes created in this tutorial\n", + "runtimes_to_delete = agentcore_control.list_agent_runtimes().get('agentRuntimes', [])\n", + "print(f\"Found {len(runtimes_to_delete)} runtimes to clean up\")\n", + "for runtime in runtimes_to_delete:\n", + " runtime_id = runtime['agentRuntimeId']\n", + " runtime_name = runtime.get('agentRuntimeName', runtime_id)\n", + " try:\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=runtime_id)\n", + " print(f\" Deleted: {runtime_name} ({runtime_id})\")\n", + " except Exception as e:\n", + " print(f\" Failed to delete {runtime_name}: {e}\")\n", + "print(\"Cleanup complete.\")\n" ] }, { @@ -86,12 +107,24 @@ "metadata": {}, "outputs": [], "source": [ - "print(\"🚀 Starting Runtime cleanup...\")\n", - "destroy_bedrock_agentcore(\n", - " config_path=Path(\".bedrock_agentcore.yaml\"),\n", - " agent_name=ORCHESTRATION_NAME,\n", - " delete_ecr_repo=True\n", - ")" + "import boto3\n", + "import json\n", + "\n", + "region = boto3.session.Session().region_name\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "# List and delete all runtimes created in this tutorial\n", + "runtimes_to_delete = agentcore_control.list_agent_runtimes().get('agentRuntimes', [])\n", + "print(f\"Found {len(runtimes_to_delete)} runtimes to clean up\")\n", + "for runtime in runtimes_to_delete:\n", + " runtime_id = runtime['agentRuntimeId']\n", + " runtime_name = runtime.get('agentRuntimeName', runtime_id)\n", + " try:\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=runtime_id)\n", + " print(f\" Deleted: {runtime_name} ({runtime_id})\")\n", + " except Exception as e:\n", + " print(f\" Failed to delete {runtime_name}: {e}\")\n", + "print(\"Cleanup complete.\")\n" ] }, { @@ -207,4 +240,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/requirements.txt b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/requirements.txt index 195c57e45..df7929ac3 100644 --- a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/01-a2a-agent-orchestrating-mcp/requirements.txt @@ -1,6 +1,5 @@ boto3==1.40.50 -bedrock-agentcore==0.1.7 -bedrock-agentcore-starter-toolkit==0.1.24 +bedrock-agentcore strands-agents[a2a] strands-agents-tools pyyaml diff --git a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/02-a2a-agent-sigv4/deploy.py b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/02-a2a-agent-sigv4/deploy.py index f4a9e0a44..d03e8cdb4 100644 --- a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/02-a2a-agent-sigv4/deploy.py +++ b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/02-a2a-agent-sigv4/deploy.py @@ -12,7 +12,6 @@ from execution-role-policy.json to the role and run again. """ -from bedrock_agentcore_starter_toolkit import Runtime from boto3.session import Session # Setup diff --git a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/02-a2a-agent-sigv4/hosting_a2a_iam_auth.ipynb b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/02-a2a-agent-sigv4/hosting_a2a_iam_auth.ipynb index 2e9e1c38f..ae2461433 100644 --- a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/02-a2a-agent-sigv4/hosting_a2a_iam_auth.ipynb +++ b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/02-a2a-agent-sigv4/hosting_a2a_iam_auth.ipynb @@ -71,11 +71,24 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "from bedrock_agentcore_starter_toolkit.operations.runtime import destroy_bedrock_agentcore\n", - "from boto3.session import Session\n", - "from pathlib import Path\n", - "import os" + "import boto3\n", + "import json\n", + "\n", + "region = boto3.session.Session().region_name\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "# List and delete all runtimes created in this tutorial\n", + "runtimes_to_delete = agentcore_control.list_agent_runtimes().get('agentRuntimes', [])\n", + "print(f\"Found {len(runtimes_to_delete)} runtimes to clean up\")\n", + "for runtime in runtimes_to_delete:\n", + " runtime_id = runtime['agentRuntimeId']\n", + " runtime_name = runtime.get('agentRuntimeName', runtime_id)\n", + " try:\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=runtime_id)\n", + " print(f\" Deleted: {runtime_name} ({runtime_id})\")\n", + " except Exception as e:\n", + " print(f\" Failed to delete {runtime_name}: {e}\")\n", + "print(\"Cleanup complete.\")\n" ] }, { @@ -202,7 +215,7 @@ "source": [ "## Configuring AgentCore Runtime Deployment\n", "\n", - "Next we will use the starter toolkit to configure the AgentCore Runtime deployment. We will configure:\n", + "Next we will use the AgentCore SDK to configure the AgentCore Runtime deployment. We will configure:\n", "\n", "* Entrypoint: `agent.py`\n", "* Auto-create execution role\n", @@ -267,12 +280,24 @@ "metadata": {}, "outputs": [], "source": [ - "print(\"Launching A2A agent to AgentCore Runtime...\")\n", - "print(\"This may take several minutes...\")\n", - "launch_result = agentcore_runtime.launch()\n", - "print(\"Launch completed ✓\")\n", - "print(f\"Agent ARN: {launch_result.agent_arn}\")\n", - "print(f\"Agent ID: {launch_result.agent_id}\")" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['agent.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -389,10 +414,24 @@ "metadata": {}, "outputs": [], "source": [ - "destroy_bedrock_agentcore(\n", - " config_path=Path(\".bedrock_agentcore.yaml\")\n", - ")\n", - "print(\"✓ AgentCore Runtime resources deleted\")" + "import boto3\n", + "import json\n", + "\n", + "region = boto3.session.Session().region_name\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "# List and delete all runtimes created in this tutorial\n", + "runtimes_to_delete = agentcore_control.list_agent_runtimes().get('agentRuntimes', [])\n", + "print(f\"Found {len(runtimes_to_delete)} runtimes to clean up\")\n", + "for runtime in runtimes_to_delete:\n", + " runtime_id = runtime['agentRuntimeId']\n", + " runtime_name = runtime.get('agentRuntimeName', runtime_id)\n", + " try:\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=runtime_id)\n", + " print(f\" Deleted: {runtime_name} ({runtime_id})\")\n", + " except Exception as e:\n", + " print(f\" Failed to delete {runtime_name}: {e}\")\n", + "print(\"Cleanup complete.\")\n" ] }, { @@ -432,4 +471,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/02-a2a-agent-sigv4/requirements.txt b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/02-a2a-agent-sigv4/requirements.txt index 36a1f1233..ddf949195 100644 --- a/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/02-a2a-agent-sigv4/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/05-hosting-a2a/02-a2a-agent-sigv4/requirements.txt @@ -1,6 +1,5 @@ boto3==1.42.28 -bedrock-agentcore==1.2.0 -bedrock-agentcore-starter-toolkit +bedrock-agentcore strands-agents[a2a] strands-agents-tools httpx diff --git a/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/02-strands-ws/websocket/requirements.txt b/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/02-strands-ws/websocket/requirements.txt index b6820a1a4..15194c599 100644 --- a/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/02-strands-ws/websocket/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/02-strands-ws/websocket/requirements.txt @@ -11,4 +11,4 @@ pyaudio==0.2.14 prompt_toolkit==3.0.52 boto3>=1.34.0 botocore>=1.34.0 -bedrock-agentcore>=1.0.0 \ No newline at end of file +bedrock-agentcore diff --git a/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/utils/cleanup.py b/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/utils/cleanup.py index 56d063a96..fa21672bb 100755 --- a/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/utils/cleanup.py +++ b/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/utils/cleanup.py @@ -345,8 +345,7 @@ def delete_gateway(self): self._print(f"\n🌐 Deleting MCP Gateway: {gateway_id}", Colors.YELLOW) try: - from bedrock_agentcore_starter_toolkit.operations.gateway.client import ( - GatewayClient, + GatewayClient, ) # Initialize Gateway client diff --git a/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/utils/deploy.py b/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/utils/deploy.py index 653e42baf..dfd71c8c8 100755 --- a/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/utils/deploy.py +++ b/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/utils/deploy.py @@ -29,8 +29,6 @@ from typing import Dict, Optional import boto3 -from bedrock_agentcore_starter_toolkit.operations.gateway.client import GatewayClient -from bedrock_agentcore_starter_toolkit.operations.runtime.launch import ( launch_bedrock_agentcore, ) diff --git a/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/utils/requirements.txt b/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/utils/requirements.txt index d91d458a1..cdf2d7af7 100644 --- a/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/utils/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming/utils/requirements.txt @@ -2,8 +2,7 @@ # Install with: pip install -r requirements.txt # AgentCore Starter Toolkit -bedrock-agentcore>=0.1.0 -bedrock-agentcore-starter-toolkit>=0.1.0 +bedrock-agentcore # AWS SDK boto3>=1.34.0 diff --git a/01-tutorials/01-AgentCore-runtime/07-mcp-dynamic-client-registration/deploy_dcr_mcp_agentcore.ipynb b/01-tutorials/01-AgentCore-runtime/07-mcp-dynamic-client-registration/deploy_dcr_mcp_agentcore.ipynb index e619e9eb9..27d64348f 100644 --- a/01-tutorials/01-AgentCore-runtime/07-mcp-dynamic-client-registration/deploy_dcr_mcp_agentcore.ipynb +++ b/01-tutorials/01-AgentCore-runtime/07-mcp-dynamic-client-registration/deploy_dcr_mcp_agentcore.ipynb @@ -325,35 +325,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "from boto3.session import Session\n", - "boto_session = Session()\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", + "\n", + "boto_session = boto3.session.Session()\n", "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", "\n", - "agentcore_runtime = Runtime()\n", "agent_name = \"mcp_dcr_sample\"\n", "\n", - "auth_config = {\n", - " \"customJWTAuthorizer\": {\n", - " \"allowedAudience\": [\n", - " AUDIENCE\n", - " ],\n", - " \"discoveryUrl\": DISCOVERY_URL,\n", - " }\n", - "}\n", - "\n", - "response = agentcore_runtime.configure(\n", - " entrypoint=\"server.py\",\n", - " auto_create_execution_role=True,\n", - " requirements_file=\"requirements.txt\",\n", - " region=region,\n", - " agent_name=agent_name,\n", - " authorizer_configuration=auth_config,\n", - " protocol=\"MCP\",\n", - " memory_mode=\"NO_MEMORY\",\n", - " deployment_type=\"direct_code_deploy\",\n", - " runtime_type=\"PYTHON_3_13\",\n", - ")" + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"server.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", + ")\n", + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -363,12 +428,24 @@ "metadata": {}, "outputs": [], "source": [ - "print(\"Launching MCP server to AgentCore Runtime...\")\n", - "print(\"This may take several minutes...\")\n", - "launch_result = agentcore_runtime.launch()\n", - "print(\"Launch completed ✓\")\n", - "print(f\"Agent ARN: {launch_result.agent_arn}\")\n", - "print(f\"Agent ID: {launch_result.agent_id}\")" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['server.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -486,4 +563,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/07-mcp-dynamic-client-registration/requirements.txt b/01-tutorials/01-AgentCore-runtime/07-mcp-dynamic-client-registration/requirements.txt index 7b5422615..c2c485380 100644 --- a/01-tutorials/01-AgentCore-runtime/07-mcp-dynamic-client-registration/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/07-mcp-dynamic-client-registration/requirements.txt @@ -1,4 +1,3 @@ -bedrock-agentcore-starter-toolkit bedrock-agentcore mcp uv diff --git a/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/01-server-e2e/mcp_server_features_e2e.ipynb b/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/01-server-e2e/mcp_server_features_e2e.ipynb index 31bc7302e..882ef9558 100644 --- a/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/01-server-e2e/mcp_server_features_e2e.ipynb +++ b/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/01-server-e2e/mcp_server_features_e2e.ipynb @@ -309,31 +309,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "\n", - "agentcore_runtime_mcp_agent = Runtime()\n", - "aws_agent_name=\"mcp_e2e_stateless_server\"\n", - "\n", - "# Configure the deployment\n", - "response_aws_mcp_agent = agentcore_runtime_mcp_agent.configure(\n", - " entrypoint=\"agents/mcp_e2e_stateless_server.py\",\n", - " execution_role=execution_role_arn_mcp,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"agents/requirements.txt\",\n", - " region=region,\n", - " agent_name=aws_agent_name,\n", - " authorizer_configuration={\n", - " \"customJWTAuthorizer\": {\n", - " \"allowedClients\": [cognito_config.get(\"client_id\")],\n", - " \"discoveryUrl\": cognito_config.get(\"discovery_url\"),\n", - " }\n", - " },\n", - " protocol=\"MCP\",\n", - " deployment_type=\"direct_code_deploy\",\n", - " runtime_type=\"PYTHON_3_13\"\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", + "\n", + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"mcp_e2e_stateless_server\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"agents/mcp_e2e_stateless_server.py\", \"agents/requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "\n", - "print(\"Configuration completed:\", response_aws_mcp_agent)" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -343,10 +412,24 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result_mcp = agentcore_runtime_mcp_agent.launch()\n", - "print(\"Launch completed:\", launch_result_mcp.agent_arn)\n", - "\n", - "mcp_arn = launch_result_mcp.agent_arn" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['agents/mcp_e2e_stateless_server.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -1244,14 +1327,24 @@ "metadata": {}, "outputs": [], "source": [ - "from pathlib import Path\n", - "from bedrock_agentcore_starter_toolkit.operations.runtime.destroy import destroy_bedrock_agentcore\n", + "import boto3\n", + "import json\n", "\n", - "print(\"🚀 Starting Runtime cleanup...\")\n", - "destroy_bedrock_agentcore(\n", - " config_path=Path(\".bedrock_agentcore.yaml\"),\n", - " agent_name=aws_agent_name\n", - ")" + "region = boto3.session.Session().region_name\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "# List and delete all runtimes created in this tutorial\n", + "runtimes_to_delete = agentcore_control.list_agent_runtimes().get('agentRuntimes', [])\n", + "print(f\"Found {len(runtimes_to_delete)} runtimes to clean up\")\n", + "for runtime in runtimes_to_delete:\n", + " runtime_id = runtime['agentRuntimeId']\n", + " runtime_name = runtime.get('agentRuntimeName', runtime_id)\n", + " try:\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=runtime_id)\n", + " print(f\" Deleted: {runtime_name} ({runtime_id})\")\n", + " except Exception as e:\n", + " print(f\" Failed to delete {runtime_name}: {e}\")\n", + "print(\"Cleanup complete.\")\n" ] }, { @@ -1335,4 +1428,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/01-server-e2e/requirements.txt b/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/01-server-e2e/requirements.txt index b69cb5e1d..2f01c9230 100644 --- a/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/01-server-e2e/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/01-server-e2e/requirements.txt @@ -1,4 +1,3 @@ mcp bedrock-agentcore -bedrock-agentcore-starter-toolkit strands-agents \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/02-client-e2e/mcp_client_features_e2e.ipynb b/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/02-client-e2e/mcp_client_features_e2e.ipynb index 07569a539..82bab76d4 100644 --- a/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/02-client-e2e/mcp_client_features_e2e.ipynb +++ b/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/02-client-e2e/mcp_client_features_e2e.ipynb @@ -280,37 +280,100 @@ "metadata": {}, "outputs": [], "source": [ - "import json\n", "import boto3\n", - "from boto3.session import Session\n", - "from bedrock_agentcore_starter_toolkit import Runtime\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", "\n", - "boto_session = Session()\n", - "sts = boto3.client('sts')\n", - "account_id = sts.get_caller_identity()['Account']\n", + "boto_session = boto3.session.Session()\n", "region = boto_session.region_name\n", - "\n", - "aws_agent_name = 'mcp_client_features'\n", - "runtime = Runtime()\n", - "\n", - "response = runtime.configure(\n", - " entrypoint='agents/mcp_client_features.py',\n", - " execution_role=execution_role_arn_mcp,\n", - " auto_create_ecr=True,\n", - " requirements_file='agents/requirements.txt',\n", - " region=region,\n", - " agent_name=aws_agent_name,\n", - " authorizer_configuration={\n", - " 'customJWTAuthorizer': {\n", - " 'allowedClients': [cognito_config.get('client_id')],\n", - " 'discoveryUrl': cognito_config.get('discovery_url'),\n", - " }\n", - " },\n", - " protocol='MCP',\n", - " deployment_type='direct_code_deploy',\n", - " runtime_type='PYTHON_3_13'\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"mcp_client_features\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"agents/mcp_client_features.py\", \"agents/requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "print('Configuration completed:', response)" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -750,14 +813,24 @@ "metadata": {}, "outputs": [], "source": [ - "from pathlib import Path\n", - "from bedrock_agentcore_starter_toolkit.operations.runtime.destroy import destroy_bedrock_agentcore\n", + "import boto3\n", + "import json\n", "\n", - "print('Destroying AgentCore runtime...')\n", - "destroy_bedrock_agentcore(\n", - " config_path=Path('.bedrock_agentcore.yaml'),\n", - " agent_name=aws_agent_name\n", - ")" + "region = boto3.session.Session().region_name\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "# List and delete all runtimes created in this tutorial\n", + "runtimes_to_delete = agentcore_control.list_agent_runtimes().get('agentRuntimes', [])\n", + "print(f\"Found {len(runtimes_to_delete)} runtimes to clean up\")\n", + "for runtime in runtimes_to_delete:\n", + " runtime_id = runtime['agentRuntimeId']\n", + " runtime_name = runtime.get('agentRuntimeName', runtime_id)\n", + " try:\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=runtime_id)\n", + " print(f\" Deleted: {runtime_name} ({runtime_id})\")\n", + " except Exception as e:\n", + " print(f\" Failed to delete {runtime_name}: {e}\")\n", + "print(\"Cleanup complete.\")\n" ] }, { @@ -842,4 +915,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/02-client-e2e/requirements.txt b/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/02-client-e2e/requirements.txt index 407318289..533f487db 100644 --- a/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/02-client-e2e/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/02-client-e2e/requirements.txt @@ -1,4 +1,3 @@ #mcp fastmcp>=2.10.0 bedrock-agentcore -bedrock-agentcore-starter-toolkit diff --git a/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/03-utilities-e2e/01_progress.ipynb b/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/03-utilities-e2e/01_progress.ipynb index 1a89ca6d1..1051d69a1 100644 --- a/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/03-utilities-e2e/01_progress.ipynb +++ b/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/03-utilities-e2e/01_progress.ipynb @@ -339,36 +339,99 @@ "outputs": [], "source": [ "import boto3\n", - "from boto3.session import Session\n", - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "\n", - "boto_session = Session()\n", - "sts = boto3.client('sts')\n", - "account_id = sts.get_caller_identity()['Account']\n", - "\n", - "aws_agent_name = 'mcp_progress_server'\n", - "runtime = Runtime()\n", - "\n", - "runtime.configure(\n", - " entrypoint='agents/mcp_progress_server.py',\n", - " execution_role=execution_role_arn,\n", - " auto_create_ecr=True,\n", - " requirements_file='agents/requirements.txt',\n", - " region=region,\n", - " agent_name=aws_agent_name,\n", - " authorizer_configuration={\n", - " 'customJWTAuthorizer': {\n", - " 'allowedClients': [cognito_config.get('client_id')],\n", - " 'discoveryUrl': cognito_config.get('discovery_url'),\n", - " }\n", - " },\n", - " protocol='MCP',\n", - " deployment_type='direct_code_deploy',\n", - " runtime_type='PYTHON_3_13'\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", + "\n", + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"mcp_progress_server\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"agents/mcp_progress_server.py\", \"agents/requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "\n", - "launch_result = runtime.launch()\n", - "print('Launch completed:', launch_result.agent_arn)" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -492,14 +555,24 @@ "metadata": {}, "outputs": [], "source": [ - "from pathlib import Path\n", - "from bedrock_agentcore_starter_toolkit.operations.runtime.destroy import destroy_bedrock_agentcore\n", + "import boto3\n", + "import json\n", "\n", - "print('Destroying AgentCore runtime...')\n", - "destroy_bedrock_agentcore(\n", - " config_path=Path('.bedrock_agentcore.yaml'),\n", - " agent_name=aws_agent_name\n", - ")" + "region = boto3.session.Session().region_name\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "# List and delete all runtimes created in this tutorial\n", + "runtimes_to_delete = agentcore_control.list_agent_runtimes().get('agentRuntimes', [])\n", + "print(f\"Found {len(runtimes_to_delete)} runtimes to clean up\")\n", + "for runtime in runtimes_to_delete:\n", + " runtime_id = runtime['agentRuntimeId']\n", + " runtime_name = runtime.get('agentRuntimeName', runtime_id)\n", + " try:\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=runtime_id)\n", + " print(f\" Deleted: {runtime_name} ({runtime_id})\")\n", + " except Exception as e:\n", + " print(f\" Failed to delete {runtime_name}: {e}\")\n", + "print(\"Cleanup complete.\")\n" ] }, { @@ -581,4 +654,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/03-utilities-e2e/requirements.txt b/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/03-utilities-e2e/requirements.txt index 407318289..533f487db 100644 --- a/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/03-utilities-e2e/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/08-mcp-e2e/03-utilities-e2e/requirements.txt @@ -1,4 +1,3 @@ #mcp fastmcp>=2.10.0 bedrock-agentcore -bedrock-agentcore-starter-toolkit diff --git a/01-tutorials/01-AgentCore-runtime/09-execute-command/01_exec_command.ipynb b/01-tutorials/01-AgentCore-runtime/09-execute-command/01_exec_command.ipynb index 65ad508e4..7faade21b 100644 --- a/01-tutorials/01-AgentCore-runtime/09-execute-command/01_exec_command.ipynb +++ b/01-tutorials/01-AgentCore-runtime/09-execute-command/01_exec_command.ipynb @@ -212,37 +212,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "\n", - "# Initialize the Runtime toolkit\n", - "agentcore_runtime_agent = Runtime()\n", - "\n", - "# Define a unique name for the agent\n", - "aws_agent_name = \"exec_cmd_sample\"\n", - "\n", - "# Configure the agent deployment with the following settings:\n", - "# - entrypoint: Path to the agent code file\n", - "# - auto_create_execution_role: Automatically create IAM role with necessary permissions\n", - "# - auto_create_ecr: Automatically create ECR repository for container images\n", - "# - requirements_file: Path to Python dependencies\n", - "# - region: AWS region for deployment\n", - "# - agent_name: Unique identifier for the agent\n", - "# - protocol: Communication protocol (HTTP for REST-like interactions)\n", - "# - deployment_type: Deploy Python code directly without Docker\n", - "# - runtime_type: Python version for the runtime environment\n", - "response_aws_agent = agentcore_runtime_agent.configure(\n", - " entrypoint=\"agents/agent.py\",\n", - " auto_create_execution_role=True,\n", - " auto_create_ecr=True,\n", - " requirements_file=\"agents/requirements.txt\",\n", - " region=region,\n", - " agent_name=aws_agent_name,\n", - " protocol=\"HTTP\",\n", - " deployment_type=\"direct_code_deploy\",\n", - " runtime_type=\"PYTHON_3_13\"\n", - ")\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", "\n", - "print(\"Configuration completed:\", response_aws_agent)" + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"exec_cmd_sample\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"agents/agent.py\", \"agents/requirements.txt\"],\n", + " region,\n", + " account_id\n", + ")\n", + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -252,19 +315,24 @@ "metadata": {}, "outputs": [], "source": [ - "# Deploy the agent to AWS\n", - "# This process:\n", - "# 1. Creates/updates IAM execution role\n", - "# 2. Packages the agent code and dependencies\n", - "# 3. Uploads to S3\n", - "# 4. Deploys to Bedrock AgentCore Runtime\n", - "# 5. Configures CloudWatch Logs and X-Ray tracing\n", - "launch_result = agentcore_runtime_agent.launch()\n", - "\n", - "print(\"Launch completed:\", launch_result.agent_arn)\n", - "\n", - "# Store the agent ARN for later invocations\n", - "cmd_agent_arn = launch_result.agent_arn" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['agents/agent.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -367,19 +435,24 @@ "metadata": {}, "outputs": [], "source": [ - "from pathlib import Path\n", - "from bedrock_agentcore_starter_toolkit.operations.runtime.destroy import destroy_bedrock_agentcore\n", - "\n", - "print(\"🚀 Starting Runtime cleanup...\")\n", - "\n", - "# Destroy the agent runtime and all associated resources\n", - "# This reads the configuration from the .bedrock_agentcore.yaml file\n", - "destroy_bedrock_agentcore(\n", - " config_path=Path(\".bedrock_agentcore.yaml\"),\n", - " agent_name=aws_agent_name\n", - ")\n", + "import boto3\n", + "import json\n", "\n", - "print(\"✅ Cleanup completed successfully!\")" + "region = boto3.session.Session().region_name\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "# List and delete all runtimes created in this tutorial\n", + "runtimes_to_delete = agentcore_control.list_agent_runtimes().get('agentRuntimes', [])\n", + "print(f\"Found {len(runtimes_to_delete)} runtimes to clean up\")\n", + "for runtime in runtimes_to_delete:\n", + " runtime_id = runtime['agentRuntimeId']\n", + " runtime_name = runtime.get('agentRuntimeName', runtime_id)\n", + " try:\n", + " agentcore_control.delete_agent_runtime(agentRuntimeId=runtime_id)\n", + " print(f\" Deleted: {runtime_name} ({runtime_id})\")\n", + " except Exception as e:\n", + " print(f\" Failed to delete {runtime_name}: {e}\")\n", + "print(\"Cleanup complete.\")\n" ] }, { @@ -433,4 +506,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/09-execute-command/requirements.txt b/01-tutorials/01-AgentCore-runtime/09-execute-command/requirements.txt index 58d98a4bd..d4b7d1ab3 100644 --- a/01-tutorials/01-AgentCore-runtime/09-execute-command/requirements.txt +++ b/01-tutorials/01-AgentCore-runtime/09-execute-command/requirements.txt @@ -1,5 +1,4 @@ boto3>=1.42.69 bedrock-agentcore -bedrock-agentcore-starter-toolkit strands-agents \ No newline at end of file diff --git a/01-tutorials/01-AgentCore-runtime/11-ag-ui-examples/hosting_agui_agent_cognito.ipynb b/01-tutorials/01-AgentCore-runtime/11-ag-ui-examples/hosting_agui_agent_cognito.ipynb index 2402d3b43..63cee447f 100644 --- a/01-tutorials/01-AgentCore-runtime/11-ag-ui-examples/hosting_agui_agent_cognito.ipynb +++ b/01-tutorials/01-AgentCore-runtime/11-ag-ui-examples/hosting_agui_agent_cognito.ipynb @@ -31,7 +31,7 @@ " \n", "\n", "\n", - "For more information: [AG-UI Protocol](https://docs.ag-ui.com) \u00b7 [AgentCore AGUI Docs](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-agui.html)" + "For more information: [AG-UI Protocol](https://docs.ag-ui.com) · [AgentCore AGUI Docs](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-agui.html)" ] }, { @@ -111,7 +111,7 @@ "source": [ "## 2. Deploy to AgentCore Runtime\n", "\n", - "The starter toolkit `configure()` accepts `protocol='AGUI'` directly \u2014 no separate update step needed." + "The AgentCore SDK `configure()` accepts `protocol='AGUI'` directly — no separate update step needed." ] }, { @@ -121,26 +121,100 @@ "metadata": {}, "outputs": [], "source": [ - "from bedrock_agentcore_starter_toolkit import Runtime\n", - "\n", - "agentcore_runtime = Runtime()\n", - "response = agentcore_runtime.configure(\n", - " entrypoint='agui_agent.py',\n", - " auto_create_execution_role=True,\n", - " protocol='AGUI',\n", - " requirements_file='requirements.txt',\n", - " region=region,\n", - " agent_name='doc_generator_agui_test_agui',\n", - " deployment_type='direct_code_deploy',\n", - " runtime_type='PYTHON_3_13',\n", - " authorizer_configuration={\n", - " 'customJWTAuthorizer': {\n", - " 'discoveryUrl': discovery_url,\n", - " 'allowedClients': [client_id],\n", - " }\n", - " },\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", + "\n", + "boto_session = boto3.session.Session()\n", + "region = boto_session.region_name\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"doc_generator_agui_test_agui\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"agui_agent.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "response" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -150,8 +224,24 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result = agentcore_runtime.launch()\n", - "launch_result" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['agui_agent.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -161,9 +251,15 @@ "metadata": {}, "outputs": [], "source": [ - "status_response = agentcore_runtime.status()\n", - "status = status_response.endpoint['status']\n", - "print(f'\\n\\u2705 Runtime status: {status}')" + "import time\n", + "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", + "status = create_response.get('status', 'CREATING')\n", + "while status not in end_status:\n", + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" ] }, { diff --git a/01-tutorials/01-AgentCore-runtime/11-ag-ui-examples/hosting_agui_agent_iam.ipynb b/01-tutorials/01-AgentCore-runtime/11-ag-ui-examples/hosting_agui_agent_iam.ipynb index 6411b8408..cbe643649 100644 --- a/01-tutorials/01-AgentCore-runtime/11-ag-ui-examples/hosting_agui_agent_iam.ipynb +++ b/01-tutorials/01-AgentCore-runtime/11-ag-ui-examples/hosting_agui_agent_iam.ipynb @@ -31,7 +31,7 @@ " \n", "\n", "\n", - "For more information: [AG-UI Protocol](https://docs.ag-ui.com) \u00b7 [AgentCore AGUI Docs](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-agui.html)" + "For more information: [AG-UI Protocol](https://docs.ag-ui.com) · [AgentCore AGUI Docs](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-agui.html)" ] }, { @@ -76,24 +76,100 @@ "metadata": {}, "outputs": [], "source": [ - "from boto3.session import Session\n", - "from bedrock_agentcore_starter_toolkit import Runtime\n", + "import boto3\n", + "import json\n", + "import zipfile\n", + "import tempfile\n", + "import os\n", + "import time\n", "\n", - "boto_session = Session()\n", + "boto_session = boto3.session.Session()\n", "region = boto_session.region_name\n", - "\n", - "agentcore_runtime = Runtime()\n", - "response = agentcore_runtime.configure(\n", - " entrypoint='agui_agent.py',\n", - " auto_create_execution_role=True,\n", - " protocol='AGUI',\n", - " requirements_file='requirements.txt',\n", - " region=region,\n", - " agent_name='doc_generator_agui_iam',\n", - " deployment_type='direct_code_deploy',\n", - " runtime_type='PYTHON_3_13',\n", + "account_id = boto3.client('sts').get_caller_identity()['Account']\n", + "agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region)\n", + "\n", + "def create_or_get_execution_role(agent_name, region, account_id):\n", + " \"\"\"Create or retrieve an IAM execution role for the AgentCore runtime.\"\"\"\n", + " iam = boto3.client('iam')\n", + " role_name = f\"AgentCoreRuntime-{agent_name[:40]}\"\n", + " trust_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [{\n", + " \"Effect\": \"Allow\",\n", + " \"Principal\": {\"Service\": \"bedrock-agentcore.amazonaws.com\"},\n", + " \"Action\": \"sts:AssumeRole\",\n", + " \"Condition\": {\n", + " \"StringEquals\": {\"aws:SourceAccount\": account_id},\n", + " \"ArnLike\": {\"aws:SourceArn\": f\"arn:aws:bedrock-agentcore:{region}:{account_id}:runtime/*\"}\n", + " }\n", + " }]\n", + " }\n", + " permissions_policy = {\n", + " \"Version\": \"2012-10-17\",\n", + " \"Statement\": [\n", + " {\"Effect\": \"Allow\", \"Action\": [\"bedrock:InvokeModel\", \"bedrock:InvokeModelWithResponseStream\"], \"Resource\": \"*\"},\n", + " {\"Effect\": \"Allow\", \"Action\": [\"logs:CreateLogGroup\", \"logs:CreateLogDelivery\", \"logs:PutLogEvents\",\n", + " \"logs:CreateLogStream\", \"logs:DescribeLogGroups\", \"logs:DescribeLogStreams\"], \"Resource\": \"*\"}\n", + " ]\n", + " }\n", + " try:\n", + " role = iam.create_role(\n", + " RoleName=role_name,\n", + " AssumeRolePolicyDocument=json.dumps(trust_policy),\n", + " Description=f\"Execution role for AgentCore runtime {agent_name}\"\n", + " )\n", + " iam.put_role_policy(RoleName=role_name, PolicyName=\"AgentCoreRuntimePermissions\",\n", + " PolicyDocument=json.dumps(permissions_policy))\n", + " print(f\"Created IAM role: {role_name}\")\n", + " time.sleep(10) # Allow IAM propagation\n", + " return role['Role']['Arn']\n", + " except iam.exceptions.EntityAlreadyExistsException:\n", + " arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n", + " print(f\"Using existing IAM role: {role_name}\")\n", + " return arn\n", + "\n", + "def package_and_upload_to_s3(agent_name, files, region, account_id):\n", + " \"\"\"Package agent code as a ZIP and upload to S3 for CodeZip deployment.\"\"\"\n", + " s3 = boto3.client('s3', region_name=region)\n", + " bucket_name = f\"bedrock-agentcore-{account_id}-{region}\"\n", + " try:\n", + " if region == 'us-east-1':\n", + " s3.create_bucket(Bucket=bucket_name)\n", + " else:\n", + " s3.create_bucket(Bucket=bucket_name,\n", + " CreateBucketConfiguration={'LocationConstraint': region})\n", + " print(f\"Created S3 bucket: {bucket_name}\")\n", + " except s3.exceptions.BucketAlreadyOwnedByYou:\n", + " print(f\"Using existing S3 bucket: {bucket_name}\")\n", + "\n", + " with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmpf:\n", + " zip_path = tmpf.name\n", + " with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:\n", + " for f in files:\n", + " if os.path.exists(f):\n", + " zf.write(f, os.path.basename(f))\n", + " print(f\" Packaged: {f}\")\n", + " s3_key = f\"{agent_name}/deployment.zip\"\n", + " s3.upload_file(zip_path, bucket_name, s3_key)\n", + " os.unlink(zip_path)\n", + " print(f\"Uploaded to s3://{bucket_name}/{s3_key}\")\n", + " return bucket_name, s3_key\n", + "\n", + "agent_name = \"doc_generator_agui_iam\"\n", + "\n", + "# Create IAM execution role\n", + "role_arn = create_or_get_execution_role(agent_name, region, account_id)\n", + "\n", + "# Package and upload agent code to S3\n", + "bucket_name, s3_key = package_and_upload_to_s3(\n", + " agent_name,\n", + " [\"agui_agent.py\", \"requirements.txt\"],\n", + " region,\n", + " account_id\n", ")\n", - "response" + "print(f\"\\nAgent name: {agent_name}\")\n", + "print(f\"Role ARN: {role_arn}\")\n", + "print(f\"S3 package: s3://{bucket_name}/{s3_key}\")\n" ] }, { @@ -103,8 +179,24 @@ "metadata": {}, "outputs": [], "source": [ - "launch_result = agentcore_runtime.launch()\n", - "launch_result" + "# Create the AgentCore Runtime using the CodeZip deployment type\n", + "create_response = agentcore_control.create_agent_runtime(\n", + " agentRuntimeName=agent_name,\n", + " agentRuntimeArtifact={\n", + " 'codeConfiguration': {\n", + " 'code': {'s3': {'bucket': bucket_name, 'prefix': s3_key}},\n", + " 'runtime': 'PYTHON_3_11',\n", + " 'entryPoint': ['agui_agent.py']\n", + " }\n", + " },\n", + " roleArn=role_arn,\n", + " networkConfiguration={'networkMode': 'PUBLIC'}\n", + ")\n", + "agent_runtime_id = create_response['agentRuntimeId']\n", + "agent_runtime_arn = create_response['agentRuntimeArn']\n", + "print(f\"AgentCore Runtime created:\")\n", + "print(f\" Runtime ID: {agent_runtime_id}\")\n", + "print(f\" Runtime ARN: {agent_runtime_arn}\")\n" ] }, { @@ -114,9 +206,15 @@ "metadata": {}, "outputs": [], "source": [ - "status_response = agentcore_runtime.status()\n", - "status = status_response.endpoint['status']\n", - "print(f'\\n\\u2705 Runtime status: {status}')" + "import time\n", + "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n", + "status = create_response.get('status', 'CREATING')\n", + "while status not in end_status:\n", + " time.sleep(15)\n", + " r = agentcore_control.get_agent_runtime(agentRuntimeId=agent_runtime_id)\n", + " status = r['status']\n", + " print(f\"Status: {status}\")\n", + "print(f\"\\nFinal status: {status}\")\n" ] }, { diff --git a/01-tutorials/01-AgentCore-runtime/README.md b/01-tutorials/01-AgentCore-runtime/README.md index e410639f1..3628ec60b 100644 --- a/01-tutorials/01-AgentCore-runtime/README.md +++ b/01-tutorials/01-AgentCore-runtime/README.md @@ -1,23 +1,54 @@ # Amazon Bedrock AgentCore Runtime ## Overview -Amazon Bedrock AgentCore Runtime is a secure, serverless runtime designed for deploying and scaling AI agents and tools. +Amazon Bedrock AgentCore Runtime is a secure, serverless runtime designed for deploying and scaling AI agents and tools. It supports any frameworks, models, and protocols, enabling developers to transform local prototypes into production-ready solutions with minimal code changes. Amazon BedrockAgentCore Python SDK provides a lightweight wrapper that helps you deploy your agent functions as HTTP services that are compatible with Amazon Bedrock. It handles all the HTTP server details so you can focus on your agent's core functionality. -All you need to do is decorate your function with the `@app.entrypoint` decorator and use the `configure` and `launch` capabilities of the SDK to deploy your agent to AgentCore Runtime. Your application is then able to invoke this agent using the SDK or any of the AWS's developer tools such as boto3, AWS SDK for JavaScript or the AWS SDK for Java. +All you need to do is decorate your function with the `@app.entrypoint` decorator and use the `create_agent_runtime` boto3 API to deploy your agent to AgentCore Runtime. Your application is then able to invoke this agent using the SDK or any of the AWS's developer tools such as boto3, AWS SDK for JavaScript or the AWS SDK for Java. ![Runtime Overview](images/runtime_overview.png) -## Key Features +--- -### Framework and Model Flexibility +## Architecture -- Deploy agents and tools from any framework (such as Strands Agents, LangChain, LangGraph, CrewAI) +Amazon Bedrock AgentCore Runtime uses a **CodeZip deployment model**: your Python agent code is packaged as a ZIP file, uploaded to S3, and the runtime is provisioned via `create_agent_runtime`. Each invocation runs in an isolated microVM with automatic scaling. + +The deployment flow: + +1. Package your agent code (Python files + `requirements.txt`) as a ZIP +2. Upload the ZIP to an S3 bucket +3. Call `create_agent_runtime` with `codeConfiguration` pointing to the S3 artifact +4. Wait for `READY` status via `get_agent_runtime` +5. Invoke via `invoke_agent_runtime` + +``` +Your Agent Code + │ + ▼ (ZIP + S3 upload) + S3 Bucket + │ + ▼ create_agent_runtime (CodeZip) +AgentCore Runtime (CREATING → READY) + │ + ▼ invoke_agent_runtime + Response +``` + +--- + +## What's This Feature + +### Key Features + +#### Framework and Model Flexibility + +- Deploy agents and tools from any framework (such as Strands Agents, LangChain, LangGraph, CrewAI) - Using any model (in Amazon Bedrock or not) -### Integration +#### Integration Amazon Bedrock AgentCore Runtime integrates with other Amazon Bedrock AgentCore capabilities through a unified SDK, including: @@ -28,7 +59,7 @@ Amazon Bedrock AgentCore Runtime integrates with other Amazon Bedrock AgentCore This integration aims to simplify the development process and provide a comprehensive platform for building, deploying, and managing AI agents. -### Use Cases +#### Use Cases The runtime is suitable for a wide range of applications, including: @@ -36,6 +67,55 @@ The runtime is suitable for a wide range of applications, including: - Long-running, complex AI workflows - Multi-modal AI processing (text, image, audio, video) +--- + +## CLI Commands + +> **CLI version**: `agentcore@0.11.0` +> +> Install or update: `npm install -g @aws/agentcore@0.11.0` + +The `agentcore` CLI provides an end-to-end workflow for creating, deploying, and managing AgentCore Runtime projects using CDK + CodeZip. + +### Create a new runtime project + +```bash +agentcore create \ + --name myagent \ + --framework Strands \ + --model-provider Bedrock \ + --build CodeZip \ + --skip-git --skip-install \ + --json +``` + +### Deploy the runtime + +```bash +cd myagent +agentcore deploy -y --json +``` + +### Check deployment status + +```bash +agentcore status --json +``` + +### Invoke the deployed agent + +```bash +agentcore invoke "Hello, what can you do?" --json +``` + +### Stream real-time logs + +```bash +agentcore logs --since 1h -n 50 +``` + +--- + ## Tutorials overview In these tutorials we will cover the following functionality: @@ -44,3 +124,30 @@ In these tutorials we will cover the following functionality: - [Hosting MCP Servers](02-hosting-MCP-server) - [Advanced Concepts](03-advanced-concepts) +--- + +## Cleanup + +**Using boto3** (recommended): + +```python +import boto3 + +region = boto3.session.Session().region_name +agentcore_control = boto3.client('bedrock-agentcore-control', region_name=region) + +# Delete the runtime +agentcore_control.delete_agent_runtime(agentRuntimeId=agent_runtime_id) + +# Delete S3 deployment artifact +s3 = boto3.client('s3', region_name=region) +s3.delete_object(Bucket=bucket_name, Key=s3_key) +``` + +**Using CLI** (when deployed via `agentcore deploy`): + +```bash +# Remove the agent from config and redeploy to destroy resources +agentcore remove agent --name myagent --json +agentcore deploy -y --json +```