184 lines
6.1 KiB
Python
Raw Permalink Normal View History

feat(market-trend-agent): market trends agent added (LangGraph sample) (#306) * 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>
2025-09-04 20:26:50 +03:00
#!/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)