mirror of
https://github.com/awslabs/amazon-bedrock-agentcore-samples.git
synced 2025-09-08 20:50:46 +00:00
* Add Market Trends Agent with Bedrock AgentCore browser integration - Implements Strands-based agent for real-time stock data and business news - Uses Bedrock AgentCore browser_client for web scraping - Includes tools for Yahoo Finance stock data and Bloomberg news search - Features professional market analysis with AI-powered insights - Supports concurrent browser sessions for multiple stock comparisons - Complete with tests, documentation, and clean requirements * feat: Add Market Trends Agent with AgentCore integration - Implement conversational broker card system with memory integration - Add browser automation for real-time market data and news - Create LangGraph-based agent architecture with tool orchestration - Include deployment scripts and comprehensive documentation - Add test scripts demonstrating broker profile workflow - Configure Docker containerization for AgentCore Runtime * feat: optimize memory management and fix deployment issues - Fix memory creation to check existing memory first, eliminating ValidationException errors - Extract memory tools into separate module for better organization - Update deployment script with enhanced error handling and IAM permissions - Add architecture documentation and diagrams - Successfully deployed to burner account with Claude Sonnet 4 and multi-strategy memory * fix: remove unsupported auto_update_on_conflict parameter and enable dynamic config - Remove auto_update_on_conflict parameter from runtime.launch() call - Allow .bedrock_agentcore.yaml to be generated dynamically by deploy script - Successfully deployed to burner account (484363822803) with Claude Sonnet 4 - All tests passing: 4/4 (100% success rate) - Agent fully functional with memory, market data, and news search capabilities * fix:image link in readme * fix:replaced image * fix: resolve all linting errors and code quality issues - Remove unused imports: tool, MemoryClient, ClientError, argparse, json, os, datetime, Any, re - Fix f-strings without placeholders by converting to regular strings - Remove unused variables: config_response, launch_result - Clean up code to pass GitHub checks and improve maintainability Fixes: - deploy.py: 4 issues (f-strings + unused variables) - market_trends_agent.py: 6 unused imports - test_broker_card.py: 2 issues (unused import + f-string) - tools/broker_card_tools.py: 2 unused imports - tools/memory_tools.py: 3 issues (unused import + 2 f-strings) * Changes to be committed: modified: 02-use-cases/market-trends-agent/.gitignore modified: 02-use-cases/market-trends-agent/tools/memory_tools.py * fix: add back datetime import needed for session_id generation The datetime import was accidentally removed but is still needed on line 27 for creating session IDs in the format 'market-{datetime.now().strftime('%Y%m%d%H%M%S')}'. * refactor: remove Dockerfile from repo, let deployment auto-generate - Remove Dockerfile from version control - Add Dockerfile to .gitignore - Deployment script will auto-generate Dockerfile with proper defaults - Reduces repo clutter while maintaining deployment functionality - Auto-generated Dockerfile will include necessary configurations * Add uv support and fix news source reliability - Add pyproject.toml with uv configuration - Update README to use only uv commands - Fix Reuters and CNBC URL issues with better error handling - Add interactive chat instructions for users - Improve browser tool with retry logic and reliable fallbacks" * Fix memory management and add uv integration - Fix broker identity confusion by enforcing identify_broker() workflow - Improve actor_id consistency across memory operations - Add enhanced error handling for news sources (Reuters/CNBC 503 errors) - Add uv integration with pyproject.toml for faster dependency management - Streamline README to uv-only workflow with interactive chat instructions - Add reliable fallback news sources (Yahoo Finance, MarketWatch) * Fix broker identification workflow and message filtering - Enhanced system prompt to enforce mandatory broker identification - Fixed tool_use/tool_result message pairing in filtering logic - Added comprehensive test suite for broker memory storage - Verified multi-strategy memory works for Tim Dunk and Maria Rodriguez * Implement distributed memory coordination using SSM Parameter Store - Added SSM Parameter Store for distributed memory ID coordination - Enhanced race condition protection for deployment scenarios - Updated IAM permissions to include SSM parameter access - Prevents multiple memory instances during AgentCore deployment - Maintains backward compatibility with local .memory_id file * Add comprehensive cleanup script and documentation - Created cleanup.py script to remove all deployed AWS resources - Cleans up AgentCore runtime, memory, ECR, CodeBuild, S3, IAM, SSM - Added safety features: dry-run mode, confirmation prompts, detailed logging - Updated README with complete cleanup section and troubleshooting - Tested cleanup successfully removes all resources except runtime (manual) - Supports selective cleanup options (--skip-iam, --region, --dry-run) * fix: resolve all lint errors in market trends agent - Remove unused imports (json, time, extract_actor_id) - Fix f-strings without placeholders in browser_tool.py - Replace bare except clauses with specific exception handling - Add proper logging for exception cases - All files now pass ruff lint checks --------- Signed-off-by: Erez Weinstein <125476602+erezweinstein5@users.noreply.github.com> Co-authored-by: Erez Weinstein <erweinst@amazon.com>
391 lines
14 KiB
Python
391 lines
14 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Complete Market Trends Agent Deployment Script
|
|
Handles IAM role creation, permissions, container deployment, and agent setup
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import logging
|
|
import boto3
|
|
import time
|
|
from pathlib import Path
|
|
|
|
# Configure logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s - %(levelname)s - %(message)s"
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class MarketTrendsAgentDeployer:
|
|
"""Complete deployer for Market Trends Agent"""
|
|
|
|
def __init__(self, region: str = "us-east-1"):
|
|
self.region = region
|
|
self.iam_client = boto3.client('iam', region_name=region)
|
|
|
|
def create_execution_role(self, role_name: str) -> str:
|
|
"""Create IAM execution role with all required permissions"""
|
|
|
|
# Trust policy for Bedrock AgentCore
|
|
trust_policy = {
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{
|
|
"Effect": "Allow",
|
|
"Principal": {
|
|
"Service": "bedrock-agentcore.amazonaws.com"
|
|
},
|
|
"Action": "sts:AssumeRole"
|
|
}
|
|
]
|
|
}
|
|
|
|
# Comprehensive execution policy with all required permissions
|
|
execution_policy = {
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": [
|
|
"bedrock:InvokeModel",
|
|
"bedrock:InvokeModelWithResponseStream"
|
|
],
|
|
"Resource": "*",
|
|
"Sid": "BedrockModelInvocation"
|
|
},
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": [
|
|
"bedrock-agentcore:*"
|
|
],
|
|
"Resource": "*",
|
|
"Sid": "BedrockAgentCoreOperations"
|
|
},
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": [
|
|
"ecr:GetAuthorizationToken",
|
|
"ecr:BatchCheckLayerAvailability",
|
|
"ecr:GetDownloadUrlForLayer",
|
|
"ecr:BatchGetImage"
|
|
],
|
|
"Resource": "*",
|
|
"Sid": "ECRAccess"
|
|
},
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": [
|
|
"xray:PutTraceSegments",
|
|
"xray:PutTelemetryRecords"
|
|
],
|
|
"Resource": "*",
|
|
"Sid": "XRayTracing"
|
|
},
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": [
|
|
"logs:CreateLogGroup",
|
|
"logs:CreateLogStream",
|
|
"logs:PutLogEvents"
|
|
],
|
|
"Resource": "*",
|
|
"Sid": "CloudWatchLogging"
|
|
},
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": [
|
|
"ssm:GetParameter",
|
|
"ssm:PutParameter",
|
|
"ssm:DeleteParameter"
|
|
],
|
|
"Resource": "arn:aws:ssm:*:*:parameter/bedrock-agentcore/market-trends-agent/*",
|
|
"Sid": "SSMParameterAccess"
|
|
}
|
|
]
|
|
}
|
|
|
|
try:
|
|
# Create the role
|
|
logger.info(f"🔐 Creating IAM role: {role_name}")
|
|
role_response = self.iam_client.create_role(
|
|
RoleName=role_name,
|
|
AssumeRolePolicyDocument=json.dumps(trust_policy),
|
|
Description="Execution role for Market Trends Agent with comprehensive permissions"
|
|
)
|
|
|
|
# Attach the comprehensive execution policy
|
|
logger.info(f"📋 Attaching comprehensive execution policy to role: {role_name}")
|
|
self.iam_client.put_role_policy(
|
|
RoleName=role_name,
|
|
PolicyName="MarketTrendsAgentComprehensivePolicy",
|
|
PolicyDocument=json.dumps(execution_policy)
|
|
)
|
|
|
|
role_arn = role_response['Role']['Arn']
|
|
logger.info(f"✅ Created IAM role with ARN: {role_arn}")
|
|
|
|
# Wait for role to propagate
|
|
logger.info("⏳ Waiting for role to propagate...")
|
|
time.sleep(10)
|
|
|
|
return role_arn
|
|
|
|
except self.iam_client.exceptions.EntityAlreadyExistsException:
|
|
logger.info(f"📋 IAM role {role_name} already exists, using existing role")
|
|
|
|
# Update the existing role with comprehensive permissions
|
|
logger.info("📋 Updating existing role with comprehensive permissions...")
|
|
self.iam_client.put_role_policy(
|
|
RoleName=role_name,
|
|
PolicyName="MarketTrendsAgentComprehensivePolicy",
|
|
PolicyDocument=json.dumps(execution_policy)
|
|
)
|
|
|
|
role_response = self.iam_client.get_role(RoleName=role_name)
|
|
return role_response['Role']['Arn']
|
|
|
|
except Exception as e:
|
|
logger.error(f"❌ Failed to create IAM role: {e}")
|
|
raise
|
|
|
|
def deploy_agent(
|
|
self,
|
|
agent_name: str,
|
|
role_name: str = "MarketTrendsAgentRole",
|
|
entrypoint: str = "market_trends_agent.py",
|
|
requirements_file: str = None
|
|
) -> str:
|
|
"""Deploy the Market Trends Agent with all requirements"""
|
|
|
|
try:
|
|
from bedrock_agentcore_starter_toolkit import Runtime
|
|
|
|
logger.info("🚀 Starting Market Trends Agent Deployment")
|
|
logger.info(f" 📝 Agent Name: {agent_name}")
|
|
logger.info(f" 📍 Region: {self.region}")
|
|
logger.info(f" 🎯 Entrypoint: {entrypoint}")
|
|
|
|
# Step 1: Determine dependency management approach
|
|
if requirements_file is None:
|
|
# Auto-detect: prefer uv if pyproject.toml exists, fallback to requirements.txt
|
|
if Path("pyproject.toml").exists():
|
|
logger.info("📦 Using uv with pyproject.toml for dependency management")
|
|
requirements_file = "pyproject.toml"
|
|
elif Path("requirements.txt").exists():
|
|
logger.info("📦 Using pip with requirements.txt for dependency management")
|
|
requirements_file = "requirements.txt"
|
|
else:
|
|
raise FileNotFoundError("No pyproject.toml or requirements.txt found")
|
|
|
|
logger.info(f" 📋 Dependencies: {requirements_file}")
|
|
|
|
# Step 2: Create execution role with all permissions
|
|
execution_role_arn = self.create_execution_role(role_name)
|
|
|
|
# Step 3: Initialize runtime
|
|
runtime = Runtime()
|
|
|
|
# Step 4: Configure the runtime
|
|
logger.info("⚙️ Configuring runtime...")
|
|
|
|
runtime.configure(
|
|
execution_role=execution_role_arn,
|
|
entrypoint=entrypoint,
|
|
requirements_file=requirements_file,
|
|
region=self.region,
|
|
agent_name=agent_name,
|
|
auto_create_ecr=True
|
|
)
|
|
|
|
logger.info("✅ Configuration completed")
|
|
|
|
# Step 4: Launch the runtime
|
|
logger.info("🚀 Launching runtime (this may take several minutes)...")
|
|
logger.info(" 📦 Building container image...")
|
|
logger.info(" ⬆️ Pushing to ECR...")
|
|
logger.info(" 🏗️ Creating AgentCore Runtime...")
|
|
|
|
runtime.launch()
|
|
|
|
logger.info("✅ Launch completed")
|
|
|
|
# Step 5: Get status and extract ARN
|
|
logger.info("📊 Getting runtime status...")
|
|
status = runtime.status()
|
|
|
|
# Extract runtime ARN
|
|
runtime_arn = None
|
|
if hasattr(status, 'agent_arn'):
|
|
runtime_arn = status.agent_arn
|
|
elif hasattr(status, 'config') and hasattr(status.config, 'agent_arn'):
|
|
runtime_arn = status.config.agent_arn
|
|
|
|
if runtime_arn:
|
|
# Save ARN to file
|
|
arn_file = Path(".agent_arn")
|
|
with open(arn_file, "w") as f:
|
|
f.write(runtime_arn)
|
|
|
|
logger.info("\n🎉 Market Trends Agent Deployed Successfully!")
|
|
logger.info(f"🏷️ Runtime ARN: {runtime_arn}")
|
|
logger.info(f"📍 Region: {self.region}")
|
|
logger.info(f"🔐 Execution Role: {execution_role_arn}")
|
|
logger.info(f"💾 ARN saved to: {arn_file}")
|
|
|
|
# Show CloudWatch logs info
|
|
agent_id = runtime_arn.split('/')[-1]
|
|
log_group = f"/aws/bedrock-agentcore/runtimes/{agent_id}-DEFAULT"
|
|
logger.info("\n📊 Monitoring:")
|
|
logger.info(f" CloudWatch Logs: {log_group}")
|
|
logger.info(f" Tail logs: aws logs tail {log_group} --follow")
|
|
|
|
logger.info("\n📋 Next Steps:")
|
|
logger.info("1. Test your agent: python test_agent.py")
|
|
logger.info("2. Monitor logs in CloudWatch")
|
|
logger.info("3. Use the Runtime ARN for integrations")
|
|
|
|
return runtime_arn
|
|
else:
|
|
logger.error("❌ Could not extract runtime ARN")
|
|
logger.info(f"Status: {status}")
|
|
return None
|
|
|
|
except ImportError:
|
|
logger.error("❌ bedrock-agentcore-starter-toolkit not installed")
|
|
if Path("pyproject.toml").exists():
|
|
logger.info("Install with: uv add bedrock-agentcore-starter-toolkit")
|
|
else:
|
|
logger.info("Install with: pip install bedrock-agentcore-starter-toolkit")
|
|
return None
|
|
except Exception as e:
|
|
logger.error(f"❌ Deployment failed: {e}")
|
|
import traceback
|
|
logger.error(f"Full error: {traceback.format_exc()}")
|
|
return None
|
|
|
|
def check_prerequisites():
|
|
"""Check if all prerequisites are met"""
|
|
logger.info("🔍 Checking prerequisites...")
|
|
|
|
# Check if required files exist
|
|
required_files = [
|
|
"market_trends_agent.py",
|
|
"tools/browser_tool.py",
|
|
"tools/broker_card_tools.py",
|
|
"tools/memory_tools.py",
|
|
"tools/__init__.py"
|
|
]
|
|
|
|
# Check for dependency files (either pyproject.toml or requirements.txt)
|
|
has_pyproject = Path("pyproject.toml").exists()
|
|
has_requirements = Path("requirements.txt").exists()
|
|
|
|
if not has_pyproject and not has_requirements:
|
|
logger.error("❌ No dependency file found (pyproject.toml or requirements.txt)")
|
|
return False
|
|
|
|
if has_pyproject:
|
|
logger.info("✅ Found pyproject.toml - will use uv for dependency management")
|
|
elif has_requirements:
|
|
logger.info("✅ Found requirements.txt - will use pip for dependency management")
|
|
|
|
missing_files = []
|
|
for file in required_files:
|
|
if not Path(file).exists():
|
|
missing_files.append(file)
|
|
|
|
if missing_files:
|
|
logger.error(f"❌ Missing required files: {missing_files}")
|
|
return False
|
|
|
|
# Check Docker/Podman
|
|
import subprocess
|
|
container_runtime = None
|
|
|
|
# Try Docker first
|
|
try:
|
|
result = subprocess.run(['docker', '--version'], capture_output=True, text=True)
|
|
if result.returncode == 0:
|
|
container_runtime = 'docker'
|
|
logger.info("✅ Docker found")
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
# Try Podman if Docker not found
|
|
if not container_runtime:
|
|
try:
|
|
result = subprocess.run(['podman', '--version'], capture_output=True, text=True)
|
|
if result.returncode == 0:
|
|
container_runtime = 'podman'
|
|
logger.info("✅ Podman found")
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
if not container_runtime:
|
|
logger.error("❌ Neither Docker nor Podman found")
|
|
logger.info("💡 Make sure Docker or Podman is installed and running")
|
|
return False
|
|
|
|
# Check AWS credentials
|
|
try:
|
|
boto3.client('sts').get_caller_identity()
|
|
logger.info("✅ AWS credentials configured")
|
|
except Exception as e:
|
|
logger.error(f"❌ AWS credentials not configured: {e}")
|
|
return False
|
|
|
|
logger.info("✅ All prerequisites met")
|
|
return True
|
|
|
|
def main():
|
|
"""Main deployment function"""
|
|
parser = argparse.ArgumentParser(
|
|
description="Deploy Market Trends Agent to Amazon Bedrock AgentCore Runtime"
|
|
)
|
|
parser.add_argument(
|
|
"--agent-name",
|
|
default="market_trends_agent",
|
|
help="Name for the agent (default: market_trends_agent)"
|
|
)
|
|
parser.add_argument(
|
|
"--role-name",
|
|
default="MarketTrendsAgentRole",
|
|
help="IAM role name (default: MarketTrendsAgentRole)"
|
|
)
|
|
parser.add_argument(
|
|
"--region",
|
|
default="us-east-1",
|
|
help="AWS region (default: us-east-1)"
|
|
)
|
|
parser.add_argument(
|
|
"--skip-checks",
|
|
action="store_true",
|
|
help="Skip prerequisite checks"
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Check prerequisites
|
|
if not args.skip_checks and not check_prerequisites():
|
|
logger.error("❌ Prerequisites not met. Fix issues above or use --skip-checks")
|
|
exit(1)
|
|
|
|
# Create deployer and deploy
|
|
deployer = MarketTrendsAgentDeployer(region=args.region)
|
|
|
|
runtime_arn = deployer.deploy_agent(
|
|
agent_name=args.agent_name,
|
|
role_name=args.role_name
|
|
)
|
|
|
|
if runtime_arn:
|
|
logger.info("\n🎯 Deployment completed successfully!")
|
|
logger.info("Run 'python test_agent.py' to test your deployed agent.")
|
|
else:
|
|
logger.error("❌ Deployment failed")
|
|
exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
main() |