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>
184 lines
6.1 KiB
Python
184 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Market Trends Agent Test Suite
|
|
Tests core functionality including memory, market analysis, and basic operations
|
|
"""
|
|
|
|
import boto3
|
|
import json
|
|
import os
|
|
import time
|
|
import logging
|
|
|
|
# Configure logging
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def load_agent_arn():
|
|
"""Load the agent ARN from file"""
|
|
arn_file = '.agent_arn'
|
|
if not os.path.exists(arn_file):
|
|
print("❌ No ARN file found. Deploy the agent first.")
|
|
return None
|
|
|
|
with open(arn_file, 'r') as f:
|
|
return f.read().strip()
|
|
|
|
def invoke_agent(runtime_arn: str, prompt: str) -> str:
|
|
"""Invoke the deployed agent with a prompt"""
|
|
try:
|
|
client = boto3.client('bedrock-agentcore', region_name='us-east-1')
|
|
|
|
response = client.invoke_agent_runtime(
|
|
agentRuntimeArn=runtime_arn,
|
|
payload=json.dumps({"prompt": prompt})
|
|
)
|
|
|
|
if 'response' in response:
|
|
return response['response'].read().decode('utf-8')
|
|
else:
|
|
return str(response)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error invoking agent: {e}")
|
|
return f"Error: {e}"
|
|
|
|
def run_simple_test(runtime_arn: str):
|
|
"""Run a simple connectivity test"""
|
|
print("🧪 SIMPLE TEST: Basic Connectivity")
|
|
print("-" * 40)
|
|
|
|
test_message = "Hello, I'm testing the agent. Can you help me?"
|
|
response = invoke_agent(runtime_arn, test_message)
|
|
|
|
success = "error" not in response.lower() and len(response) > 50
|
|
print(f"✅ Response: {response[:200]}..." if len(response) > 200 else response)
|
|
print(f"🔍 Test Result: {'✅ PASSED' if success else '❌ FAILED'}")
|
|
print()
|
|
|
|
return success
|
|
|
|
def run_comprehensive_tests(runtime_arn: str):
|
|
"""Run comprehensive functionality tests"""
|
|
print("🚀 Market Trends Agent - Comprehensive Test Suite")
|
|
print("=" * 60)
|
|
print(f"📋 Testing ARN: {runtime_arn}")
|
|
print()
|
|
|
|
tests_passed = 0
|
|
total_tests = 4
|
|
|
|
# Test 1: Broker Introduction & Memory
|
|
print("📋 TEST 1: Broker Profile & Memory")
|
|
print("-" * 30)
|
|
|
|
broker_intro = "Hi, I'm Sarah Chen from Morgan Stanley. I focus on growth investing and tech stocks for younger clients. Please remember my profile."
|
|
|
|
response1 = invoke_agent(runtime_arn, broker_intro)
|
|
print("✅ Response:", response1[:200] + "..." if len(response1) > 200 else response1)
|
|
|
|
# Check if profile was acknowledged
|
|
profile_acknowledged = any(keyword in response1.lower() for keyword in ['sarah', 'morgan stanley', 'growth', 'tech', 'profile', 'remember'])
|
|
print(f"🔍 Profile Acknowledged: {'✅ YES' if profile_acknowledged else '❌ NO'}")
|
|
|
|
if profile_acknowledged:
|
|
tests_passed += 1
|
|
|
|
print()
|
|
time.sleep(5) # Wait to avoid throttling
|
|
|
|
# Test 2: Memory Recall
|
|
print("📋 TEST 2: Memory Recall")
|
|
print("-" * 30)
|
|
|
|
memory_test = "Hi, I'm Sarah Chen from Morgan Stanley. What do you remember about my investment preferences?"
|
|
response2 = invoke_agent(runtime_arn, memory_test)
|
|
print("✅ Response:", response2[:200] + "..." if len(response2) > 200 else response2)
|
|
|
|
# Check if memory was recalled
|
|
memory_recalled = any(keyword in response2.lower() for keyword in ['sarah', 'growth', 'tech', 'morgan stanley'])
|
|
print(f"🔍 Memory Recalled: {'✅ YES' if memory_recalled else '❌ NO'}")
|
|
|
|
if memory_recalled:
|
|
tests_passed += 1
|
|
|
|
print()
|
|
time.sleep(5) # Wait to avoid throttling
|
|
|
|
# Test 3: Market Data Request
|
|
print("📋 TEST 3: Market Data Request")
|
|
print("-" * 30)
|
|
|
|
market_request = "Get me the current Apple stock price and recent performance"
|
|
response3 = invoke_agent(runtime_arn, market_request)
|
|
print("✅ Response:", response3[:200] + "..." if len(response3) > 200 else response3)
|
|
|
|
# Check if market data was attempted
|
|
market_data_attempted = any(keyword in response3.lower() for keyword in ['apple', 'aapl', 'stock', 'price', 'market'])
|
|
print(f"🔍 Market Data Retrieved: {'✅ YES' if market_data_attempted else '❌ NO'}")
|
|
|
|
if market_data_attempted:
|
|
tests_passed += 1
|
|
|
|
print()
|
|
time.sleep(5) # Wait to avoid throttling
|
|
|
|
# Test 4: News Search
|
|
print("📋 TEST 4: News Search")
|
|
print("-" * 30)
|
|
|
|
news_request = "Find recent news about AI and technology stocks"
|
|
response4 = invoke_agent(runtime_arn, news_request)
|
|
print("✅ Response:", response4[:200] + "..." if len(response4) > 200 else response4)
|
|
|
|
# Check if news search was attempted
|
|
news_retrieved = any(keyword in response4.lower() for keyword in ['news', 'ai', 'technology', 'search', 'recent'])
|
|
print(f"🔍 News Retrieved: {'✅ YES' if news_retrieved else '❌ NO'}")
|
|
|
|
if news_retrieved:
|
|
tests_passed += 1
|
|
|
|
print()
|
|
|
|
# Summary
|
|
print("=" * 60)
|
|
print("📊 TEST SUMMARY")
|
|
print("=" * 60)
|
|
print(f"Tests Passed: {tests_passed}/{total_tests}")
|
|
print(f"Success Rate: {(tests_passed/total_tests)*100:.0f}%")
|
|
|
|
if tests_passed == total_tests:
|
|
print("🎉 ALL TESTS PASSED - Agent is fully functional!")
|
|
elif tests_passed >= total_tests // 2:
|
|
print("⚠️ PARTIAL SUCCESS - Some features may need attention")
|
|
else:
|
|
print("❌ ISSUES DETECTED - Agent needs attention")
|
|
|
|
return tests_passed == total_tests
|
|
|
|
def main():
|
|
"""Main test function"""
|
|
runtime_arn = load_agent_arn()
|
|
if not runtime_arn:
|
|
return False
|
|
|
|
print("Choose test type:")
|
|
print("1. Simple connectivity test")
|
|
print("2. Comprehensive functionality tests")
|
|
|
|
try:
|
|
choice = input("Enter choice (1 or 2, default=1): ").strip()
|
|
if not choice:
|
|
choice = "1"
|
|
except KeyboardInterrupt:
|
|
print("\nTest cancelled.")
|
|
return False
|
|
|
|
if choice == "2":
|
|
return run_comprehensive_tests(runtime_arn)
|
|
else:
|
|
return run_simple_test(runtime_arn)
|
|
|
|
if __name__ == "__main__":
|
|
success = main()
|
|
exit(0 if success else 1) |