diff --git a/.env.example b/.env.example index a79d8c47..a48e11b4 100644 --- a/.env.example +++ b/.env.example @@ -1,17 +1,100 @@ -# Trading Agents Environment Variables +# TradingAgents Environment Configuration # Copy this file to .env and fill in your values -# Finnhub API Key (required for market data) +# ============================================================================= +# API Keys (Required) +# ============================================================================= +OPENAI_API_KEY=your_openai_api_key_here FINNHUB_API_KEY=your_finnhub_api_key_here -# OpenAI API Key (required for AI processing) -OPENAI_API_KEY=your_openai_api_key_here +# Optional API Keys +OPENROUTER_API_KEY=your_openrouter_api_key_here +GOOGLE_API_KEY=your_google_api_key_here +ANTHROPIC_API_KEY=your_anthropic_api_key_here -# Reddit API Credentials (required for social media analysis) -REDDIT_CLIENT_ID=your_reddit_client_id_here -REDDIT_CLIENT_SECRET=your_reddit_client_secret_here +# Reddit API (Optional - for social media analysis) +REDDIT_CLIENT_ID=your_reddit_client_id +REDDIT_CLIENT_SECRET=your_reddit_client_secret REDDIT_USER_AGENT=TradingAgents/1.0 +# ============================================================================= +# SSL/TLS Certificate Configuration (OPTIONAL) +# ============================================================================= + +# Certificate Bundle Path (ONLY set if you need a custom certificate bundle) +# If not set, system default SSL behavior is used +# Common locations: +# - macOS: /etc/ssl/cert.pem +# - Ubuntu/Debian: /etc/ssl/certs/ca-certificates.crt +# - CentOS/RHEL: /etc/pki/tls/certs/ca-bundle.crt +# - Custom: /path/to/your/custom-ca-bundle.crt +# REQUESTS_CA_BUNDLE=/etc/ssl/cert.pem +# CURL_CA_BUNDLE=/etc/ssl/cert.pem + +# SSL Verification (ONLY set to false if needed for development/testing) +# If not set, SSL verification is enabled by default (recommended) +# SSL_VERIFY=false + +# HTTP Timeout (ONLY set if default timeout is insufficient) +# If not set, uses reasonable defaults +# HTTP_TIMEOUT=60 + +# ============================================================================= +# Proxy Configuration (ONLY if behind corporate firewall) +# ============================================================================= + +# HTTP/HTTPS Proxy Settings (ONLY set if required by your network) +# If not set, direct connections are used +# HTTP_PROXY=http://proxy.company.com:8080 +# HTTPS_PROXY=https://proxy.company.com:8080 + +# Proxy with authentication +# HTTP_PROXY=http://username:password@proxy.company.com:8080 +# HTTPS_PROXY=https://username:password@proxy.company.com:8080 + +# ============================================================================= +# Application Settings +# ============================================================================= + +# Results Directory +TRADINGAGENTS_RESULTS_DIR=./results + +# Ollama Configuration (if using local Ollama) +OLLAMA_HOST=localhost + # Optional Configuration # DEBUG=True -# LOG_LEVEL=INFO \ No newline at end of file +# LOG_LEVEL=INFO + +# ============================================================================= +# SSL Certificate Examples for Common Enterprise Environments +# ============================================================================= + +# Example 1: Using system certificate store (macOS) +# REQUESTS_CA_BUNDLE=/System/Library/OpenSSL/certs/cert.pem + +# Example 2: Using system certificate store (Ubuntu/Debian) +# REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt + +# Example 3: Using custom corporate certificate bundle +# REQUESTS_CA_BUNDLE=/usr/local/share/ca-certificates/corporate-ca-bundle.crt + +# Example 4: Disabling SSL verification (development only) +# SSL_VERIFY=false + +# ============================================================================= +# Troubleshooting SSL Issues +# ============================================================================= + +# If you encounter SSL certificate errors: +# 1. Run the diagnostic tool: python diagnose_ssl.py +# 2. Check if your organization uses a custom CA +# 3. Ask your IT department for the corporate certificate bundle +# 4. Try using certifi's bundle: pip install certifi +# 5. Set REQUESTS_CA_BUNDLE to certifi's location (usually shown by diagnose_ssl.py) + +# Common SSL Error Solutions: +# - "certificate verify failed": Set REQUESTS_CA_BUNDLE to correct cert bundle +# - "SSL: WRONG_VERSION_NUMBER": Check if you're behind a proxy +# - "Connection timeout": Increase HTTP_TIMEOUT or check proxy settings +# - "Name or service not known": Check DNS settings and proxy configuration \ No newline at end of file diff --git a/SSL_CONFIGURATION.md b/SSL_CONFIGURATION.md new file mode 100644 index 00000000..39957ef3 --- /dev/null +++ b/SSL_CONFIGURATION.md @@ -0,0 +1,117 @@ +# SSL Certificate Bundle Configuration for TradingAgents + +## Overview + +This implementation provides flexible SSL/TLS certificate configuration for TradingAgents while maintaining backward compatibility. The system only applies custom SSL settings when explicitly configured via environment variables. + +## Key Features + +### 1. Environment Variable Based Configuration +- `REQUESTS_CA_BUNDLE` or `CURL_CA_BUNDLE`: Path to custom certificate bundle +- `SSL_VERIFY`: Enable/disable SSL verification (true/false) +- `HTTP_TIMEOUT`: Custom timeout for HTTP requests (seconds) +- `HTTP_PROXY`: HTTP proxy server +- `HTTPS_PROXY`: HTTPS proxy server + +### 2. Default Behavior Preservation +- **If no environment variables are set**: Uses system default SSL behavior +- **Only applies custom settings when explicitly configured** +- **Empty or undefined variables are ignored** + +### 3. Comprehensive Coverage +- **LangChain LLM clients**: Custom SSL configuration for OpenAI, OpenRouter, etc. +- **HTTP requests**: Custom configuration for Google News, Reddit APIs +- **Global SSL setup**: Sets environment variables for libraries that respect them + +## Usage Examples + +### Basic Usage (No Custom SSL) +```bash +# No SSL environment variables set +# Uses system default SSL behavior +python webapp/main.py +``` + +### Custom Certificate Bundle +```bash +# Use custom corporate certificate bundle +export REQUESTS_CA_BUNDLE=/path/to/corporate-ca-bundle.crt +python webapp/main.py +``` + +### Development/Testing (Disable SSL Verification) +```bash +# Disable SSL verification (NOT recommended for production) +export SSL_VERIFY=false +python webapp/main.py +``` + +### Behind Corporate Proxy +```bash +# Configure proxy settings +export HTTP_PROXY=http://proxy.company.com:8080 +export HTTPS_PROXY=https://proxy.company.com:8080 +export REQUESTS_CA_BUNDLE=/etc/ssl/corporate-ca-bundle.crt +python webapp/main.py +``` + +## Files Modified + +### Core Configuration +- `tradingagents/default_config.py`: Added SSL configuration parameters +- `tradingagents/dataflows/ssl_utils.py`: SSL utility functions (NEW) + +### Integration Points +- `tradingagents/graph/trading_graph.py`: LLM client SSL configuration +- `tradingagents/dataflows/googlenews_utils.py`: HTTP requests SSL configuration +- `tradingagents/dataflows/interface.py`: Integration with SSL configuration + +### Documentation and Tools +- `.env.example`: Updated with SSL configuration examples +- `diagnose_ssl.py`: SSL diagnostic tool (NEW) +- `test_ssl_config.py`: SSL configuration test suite (NEW) + +## Testing + +Run the diagnostic tool to check your SSL configuration: +```bash +python diagnose_ssl.py +``` + +Run the test suite to verify SSL configuration behavior: +```bash +python test_ssl_config.py +``` + +## Troubleshooting + +### Common SSL Errors and Solutions + +1. **Certificate verification failed** + - Set `REQUESTS_CA_BUNDLE` to correct certificate bundle path + - Check if your organization uses custom CA certificates + +2. **SSL: WRONG_VERSION_NUMBER** + - Usually indicates proxy configuration issues + - Set appropriate `HTTP_PROXY` and `HTTPS_PROXY` variables + +3. **Connection timeout** + - Increase `HTTP_TIMEOUT` value + - Check network connectivity and proxy settings + +4. **Name or service not known** + - Check DNS settings + - Verify proxy configuration + +### Getting Help + +1. Run `python diagnose_ssl.py` for comprehensive SSL diagnostics +2. Check your organization's IT documentation for certificate bundles +3. Contact your IT department for corporate proxy and certificate information + +## Security Considerations + +- **Never disable SSL verification in production** +- **Use custom certificate bundles for corporate environments** +- **Keep certificate bundles updated** +- **Secure proxy credentials if using authenticated proxies** \ No newline at end of file diff --git a/combine_certificates.py b/combine_certificates.py new file mode 100644 index 00000000..1040171a --- /dev/null +++ b/combine_certificates.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +""" +Certificate Bundle Combiner for TradingAgents + +This script combines your corporate certificate bundle (Netskope) with +the certifi certificate bundle to ensure all certificates are available. +""" + +import os +import shutil +from pathlib import Path + +def combine_certificate_bundles(): + """Combine corporate and certifi certificate bundles""" + + print("๐Ÿ”— Certificate Bundle Combiner") + print("=" * 40) + + # Paths + corporate_bundle = "/Users/kevin.bruton/netskope-certificates/netskope-cert-bundle.pem" + + try: + import certifi + certifi_bundle = certifi.where() + except ImportError: + print("โŒ certifi package not found. Please install it: pip install certifi") + return False + + combined_bundle = "/Users/kevin.bruton/netskope-certificates/combined-cert-bundle.pem" + + print(f"๐Ÿ“‹ Corporate bundle: {corporate_bundle}") + print(f"๐Ÿ“‹ Certifi bundle: {certifi_bundle}") + print(f"๐Ÿ“‹ Combined bundle: {combined_bundle}") + + # Check if corporate bundle exists + if not os.path.exists(corporate_bundle): + print(f"โŒ Corporate certificate bundle not found: {corporate_bundle}") + return False + + # Create combined bundle + try: + with open(combined_bundle, 'w') as combined_file: + # Write corporate certificates first + print("๐Ÿ“ Adding corporate certificates...") + with open(corporate_bundle, 'r') as corp_file: + combined_file.write(corp_file.read()) + + # Add separator + combined_file.write("\n# Certifi certificates below\n") + + # Write certifi certificates + print("๐Ÿ“ Adding certifi certificates...") + with open(certifi_bundle, 'r') as certifi_file: + certifi_content = certifi_file.read() + combined_file.write(certifi_content) + + print(f"โœ… Combined certificate bundle created: {combined_bundle}") + + # Set permissions + os.chmod(combined_bundle, 0o644) + + # Show usage instructions + print("\n๐Ÿ’ก Usage Instructions:") + print(f" Add this to your .env file:") + print(f" REQUESTS_CA_BUNDLE={combined_bundle}") + print(f" CURL_CA_BUNDLE={combined_bundle}") + + print("\n Or export in your shell:") + print(f" export REQUESTS_CA_BUNDLE={combined_bundle}") + print(f" export CURL_CA_BUNDLE={combined_bundle}") + + return True + + except Exception as e: + print(f"โŒ Error creating combined bundle: {e}") + return False + +def test_combined_bundle(): + """Test the combined certificate bundle""" + combined_bundle = "/Users/kevin.bruton/netskope-certificates/combined-cert-bundle.pem" + + if not os.path.exists(combined_bundle): + print("โŒ Combined bundle not found. Run combine_certificate_bundles() first.") + return False + + print(f"\n๐Ÿงช Testing combined certificate bundle: {combined_bundle}") + + import requests + test_urls = [ + "https://www.google.com", + "https://api.openai.com/v1/models", + "https://openrouter.ai/api/v1/models" + ] + + for url in test_urls: + try: + response = requests.get(url, verify=combined_bundle, timeout=10) + print(f"โœ… {url} - Status: {response.status_code}") + except Exception as e: + print(f"โŒ {url} - Error: {e}") + + return True + +if __name__ == "__main__": + if combine_certificate_bundles(): + test_combined_bundle() \ No newline at end of file diff --git a/debug_streaming.py b/debug_streaming.py new file mode 100644 index 00000000..82c5e2cd --- /dev/null +++ b/debug_streaming.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +""" +Debug script to test the TradingAgentsGraph streaming behavior +""" + +import os +from dotenv import load_dotenv +from tradingagents.graph.trading_graph import TradingAgentsGraph +from tradingagents.default_config import DEFAULT_CONFIG + +# Load environment variables +load_dotenv() + +def debug_callback(state): + """Debug callback to see what state is being passed""" + print(f"\n๐Ÿ” CALLBACK RECEIVED:") + print(f" State type: {type(state)}") + print(f" State keys: {list(state.keys()) if isinstance(state, dict) else 'Not a dict'}") + + if isinstance(state, dict): + for key, value in state.items(): + if key in ["__end__", "messages"]: + continue + print(f" {key}: {type(value)} - {str(value)[:100]}...") + print("-" * 50) + +def test_streaming(): + """Test the streaming functionality""" + print("๐Ÿš€ Testing TradingAgentsGraph streaming...") + + # Create a minimal config for testing + config = DEFAULT_CONFIG.copy() + config["llm_provider"] = "openai" + config["quick_think_llm"] = "gpt-3.5-turbo" + config["deep_think_llm"] = "gpt-4" + + try: + # Initialize the graph + print("๐Ÿ“Š Initializing TradingAgentsGraph...") + graph = TradingAgentsGraph(config=config) + + # Test propagation with callback + print("๐Ÿ”„ Starting propagation with callback...") + final_state, signal = graph.propagate( + company_name="AAPL", + trade_date="2024-01-01", + on_step_callback=debug_callback + ) + + print(f"โœ… Propagation completed!") + print(f"๐Ÿ“ˆ Final signal: {signal}") + print(f"๐ŸŽฏ Final state keys: {list(final_state.keys())}") + + except Exception as e: + print(f"โŒ Error during streaming test: {e}") + import traceback + traceback.print_exc() + +if __name__ == "__main__": + test_streaming() \ No newline at end of file diff --git a/diagnose_ssl.py b/diagnose_ssl.py new file mode 100644 index 00000000..e9759502 --- /dev/null +++ b/diagnose_ssl.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +""" +SSL Certificate Diagnostic Tool for TradingAgents + +This script helps diagnose SSL/TLS certificate issues and provides guidance +on how to configure certificate bundles properly. +""" + +import os +import sys +import ssl +import socket +import requests +from urllib.parse import urlparse +from dotenv import load_dotenv +load_dotenv() +from tradingagents.dataflows.ssl_utils import get_certificate_info, get_ssl_config +from tradingagents.default_config import DEFAULT_CONFIG + + +def test_ssl_connection(hostname, port=443): + """Test SSL connection to a specific hostname.""" + print(f"\n๐Ÿ”’ Testing SSL connection to {hostname}:{port}") + + try: + # Create SSL context + context = ssl.create_default_context() + + # Connect and get certificate info + with socket.create_connection((hostname, port), timeout=10) as sock: + with context.wrap_socket(sock, server_hostname=hostname) as ssock: + cert = ssock.getpeercert() + print(f"โœ… SSL connection successful") + print(f" Subject: {cert.get('subject', 'Unknown')}") + print(f" Issuer: {cert.get('issuer', 'Unknown')}") + print(f" Version: {cert.get('version', 'Unknown')}") + return True + + except Exception as e: + print(f"โŒ SSL connection failed: {e}") + return False + + +def test_requests_connection(url): + """Test HTTP request with requests library.""" + print(f"\n๐ŸŒ Testing HTTP request to {url}") + + try: + response = requests.get(url, timeout=10) + print(f"โœ… HTTP request successful") + print(f" Status: {response.status_code}") + print(f" SSL Cert: {response.raw.connection.sock.getpeercert().get('subject', 'Unknown') if hasattr(response.raw.connection, 'sock') else 'Unknown'}") + return True + + except requests.exceptions.SSLError as e: + print(f"โŒ SSL Error: {e}") + return False + except Exception as e: + print(f"โŒ Request failed: {e}") + return False + + +def test_with_custom_cert_bundle(url, cert_bundle_path): + """Test HTTP request with custom certificate bundle.""" + print(f"\n๐Ÿ” Testing with custom cert bundle: {cert_bundle_path}") + + if not os.path.exists(cert_bundle_path): + print(f"โŒ Certificate bundle not found: {cert_bundle_path}") + return False + + try: + response = requests.get(url, verify=cert_bundle_path, timeout=10) + print(f"โœ… Request with custom cert bundle successful") + print(f" Status: {response.status_code}") + return True + + except Exception as e: + print(f"โŒ Request with custom cert bundle failed: {e}") + return False + + +def main(): + """Main diagnostic function.""" + print("๐Ÿ” TradingAgents SSL Certificate Diagnostic Tool") + print("=" * 50) + + # Get certificate information + print("\n๐Ÿ“‹ Certificate Bundle Information:") + cert_info = get_certificate_info() + for key, value in cert_info.items(): + if isinstance(value, list): + print(f" {key}: {', '.join(value) if value else 'None found'}") + else: + print(f" {key}: {value}") + + # Test SSL configuration + print(f"\nโš™๏ธ Current SSL Configuration:") + ssl_config = get_ssl_config(DEFAULT_CONFIG) + for key, value in ssl_config.items(): + print(f" {key}: {value}") + + # Test common endpoints + test_endpoints = [ + ("api.openai.com", 443), + ("openrouter.ai", 443), + ("generativelanguage.googleapis.com", 443), + ("www.google.com", 443) + ] + + print(f"\n๐ŸŽฏ Testing SSL connections:") + for hostname, port in test_endpoints: + test_ssl_connection(hostname, port) + + # Test HTTP requests + test_urls = [ + "https://api.openai.com/v1/models", + "https://www.google.com/search?q=test", + "https://openrouter.ai/api/v1/models" + ] + + print(f"\n๐ŸŒ Testing HTTP requests:") + for url in test_urls: + test_requests_connection(url) + + # Test with different certificate bundles + if cert_info.get("certifi_bundle") and cert_info["certifi_bundle"] != "Not available (certifi not installed)": + print(f"\n๐Ÿงช Testing with certifi bundle:") + test_with_custom_cert_bundle("https://www.google.com", cert_info["certifi_bundle"]) + + # Provide recommendations + print(f"\n๐Ÿ’ก Recommendations:") + print(" ๐Ÿ“‹ Certificate Bundle Configuration:") + print(" โ€ข Only set if you need a custom certificate bundle") + print(" โ€ข If not set, system default SSL behavior is used") + print(" export REQUESTS_CA_BUNDLE=/path/to/your/ca-bundle.crt") + print(" export CURL_CA_BUNDLE=/path/to/your/ca-bundle.crt") + + print("\n โš ๏ธ SSL Verification (use with caution):") + print(" โ€ข Only disable for development/testing") + print(" โ€ข If not set, SSL verification is enabled by default") + print(" export SSL_VERIFY=false") + + print("\n โฑ๏ธ Timeout Configuration:") + print(" โ€ข Only set if default timeout is insufficient") + print(" export HTTP_TIMEOUT=60") + + print("\n ๐ŸŒ Proxy Configuration:") + print(" โ€ข Only required if behind corporate firewall") + print(" export HTTP_PROXY=http://proxy.company.com:8080") + print(" export HTTPS_PROXY=https://proxy.company.com:8080") + + print("\n ๐Ÿ“ Configuration:") + print(" โ€ข Add these to your .env file or export in shell") + print(" โ€ข Leave unset to use system defaults") + print(" โ€ข Only configure what you actually need") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test_ssl_config.py b/test_ssl_config.py new file mode 100644 index 00000000..81e344ce --- /dev/null +++ b/test_ssl_config.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +""" +Test SSL configuration behavior +""" + +import os +from tradingagents.default_config import DEFAULT_CONFIG +from tradingagents.dataflows.ssl_utils import get_ssl_config, setup_global_ssl_config + +def test_ssl_config(): + """Test SSL configuration with different environment variable settings""" + + print("๐Ÿงช Testing SSL Configuration Behavior") + print("=" * 50) + + # Test 1: No environment variables set + print("\n1๏ธโƒฃ Test: No SSL environment variables set") + os.environ.pop("REQUESTS_CA_BUNDLE", None) + os.environ.pop("CURL_CA_BUNDLE", None) + os.environ.pop("SSL_VERIFY", None) + os.environ.pop("HTTP_TIMEOUT", None) + + from tradingagents.default_config import DEFAULT_CONFIG + config = DEFAULT_CONFIG.copy() + ssl_config = get_ssl_config(config) + print(f" SSL Config: {ssl_config}") + print(f" Expected: Empty or minimal config (default behavior)") + + # Test 2: Custom certificate bundle set + print("\n2๏ธโƒฃ Test: Custom certificate bundle set") + os.environ["REQUESTS_CA_BUNDLE"] = "/custom/path/ca-bundle.crt" + + # Re-import to get updated config + from importlib import reload + import tradingagents.default_config + reload(tradingagents.default_config) + from tradingagents.default_config import DEFAULT_CONFIG + + config = DEFAULT_CONFIG.copy() + ssl_config = get_ssl_config(config) + print(f" Config ssl_cert_bundle: {config.get('ssl_cert_bundle')}") + print(f" SSL Config: {ssl_config}") + print(f" Expected: cert_bundle and verify set to custom path") + + # Test 3: SSL verification disabled + print("\n3๏ธโƒฃ Test: SSL verification disabled") + os.environ.pop("REQUESTS_CA_BUNDLE", None) + os.environ["SSL_VERIFY"] = "false" + + reload(tradingagents.default_config) + from tradingagents.default_config import DEFAULT_CONFIG + + config = DEFAULT_CONFIG.copy() + ssl_config = get_ssl_config(config) + print(f" Config ssl_verify: {config.get('ssl_verify')}") + print(f" SSL Config: {ssl_config}") + print(f" Expected: verify set to False") + + # Test 4: Timeout and proxy settings + print("\n4๏ธโƒฃ Test: Timeout and proxy settings") + os.environ["HTTP_TIMEOUT"] = "60" + os.environ["HTTP_PROXY"] = "http://proxy.example.com:8080" + os.environ["HTTPS_PROXY"] = "https://proxy.example.com:8080" + + reload(tradingagents.default_config) + from tradingagents.default_config import DEFAULT_CONFIG + + config = DEFAULT_CONFIG.copy() + ssl_config = get_ssl_config(config) + print(f" Config timeout: {config.get('http_timeout')}") + print(f" Config proxies: HTTP={config.get('http_proxy')}, HTTPS={config.get('https_proxy')}") + print(f" SSL Config: {ssl_config}") + print(f" Expected: timeout and proxies in ssl_config") + + # Test 5: Empty environment variables (should not be used) + print("\n5๏ธโƒฃ Test: Empty environment variables") + os.environ["REQUESTS_CA_BUNDLE"] = "" + os.environ["HTTP_TIMEOUT"] = "" + + reload(tradingagents.default_config) + from tradingagents.default_config import DEFAULT_CONFIG + + config = DEFAULT_CONFIG.copy() + ssl_config = get_ssl_config(config) + print(f" Config ssl_cert_bundle: '{config.get('ssl_cert_bundle')}'") + print(f" Config http_timeout: {config.get('http_timeout')}") + print(f" SSL Config: {ssl_config}") + print(f" Expected: Empty values should not be used") + + # Clean up + for var in ["REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE", "SSL_VERIFY", "HTTP_TIMEOUT", "HTTP_PROXY", "HTTPS_PROXY"]: + os.environ.pop(var, None) + + print("\nโœ… SSL configuration tests completed") + +if __name__ == "__main__": + test_ssl_config() \ No newline at end of file diff --git a/test_ssl_connectivity.py b/test_ssl_connectivity.py new file mode 100644 index 00000000..2c8ebfe4 --- /dev/null +++ b/test_ssl_connectivity.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +""" +Test SSL connectivity for TradingAgents components +""" + +import os +from dotenv import load_dotenv + +# Load environment variables +load_dotenv() + +def test_api_connections(): + """Test API connections that TradingAgents will use""" + + print("๐Ÿ” Testing TradingAgents API Connections") + print("=" * 50) + + # Test 1: Basic HTTP requests with proper SSL + print("\n1๏ธโƒฃ Testing HTTP requests with SSL configuration:") + + import requests + + test_endpoints = [ + ("OpenAI API", "https://api.openai.com/v1/models"), + ("Google Search", "https://www.google.com/search?q=AAPL"), + ("OpenRouter API", "https://openrouter.ai/api/v1/models"), + ] + + for name, url in test_endpoints: + try: + response = requests.get(url, timeout=10) + print(f" โœ… {name}: Status {response.status_code}") + except Exception as e: + print(f" โŒ {name}: {e}") + + # Test 2: LangChain LLM initialization + print("\n2๏ธโƒฃ Testing LangChain LLM initialization:") + + try: + from langchain_openai import ChatOpenAI + + # Test with the SSL configuration + llm = ChatOpenAI( + model="gpt-3.5-turbo", + api_key=os.getenv("OPENAI_API_KEY", "test-key") + ) + print(" โœ… ChatOpenAI initialization successful") + + # Test a simple API call (this might fail due to API key, but SSL should work) + try: + # This will test SSL connectivity + response = llm.invoke("Hello") + print(" โœ… ChatOpenAI API call successful") + except Exception as e: + if "401" in str(e) or "Unauthorized" in str(e): + print(" โœ… ChatOpenAI SSL working (401 = API key issue, not SSL)") + else: + print(f" โš ๏ธ ChatOpenAI API call error: {e}") + + except Exception as e: + print(f" โŒ ChatOpenAI initialization failed: {e}") + + # Test 3: TradingAgents configuration + print("\n3๏ธโƒฃ Testing TradingAgents SSL configuration:") + + try: + from tradingagents.default_config import DEFAULT_CONFIG + from tradingagents.dataflows.ssl_utils import get_ssl_config, setup_global_ssl_config + + print(f" ๐Ÿ“‹ SSL cert bundle: {DEFAULT_CONFIG.get('ssl_cert_bundle')}") + print(f" ๐Ÿ“‹ SSL verify: {DEFAULT_CONFIG.get('ssl_verify')}") + + ssl_config = get_ssl_config(DEFAULT_CONFIG) + print(f" ๐Ÿ“‹ SSL config: {ssl_config}") + + # Set up global SSL configuration + setup_global_ssl_config(DEFAULT_CONFIG) + print(" โœ… Global SSL configuration applied") + + except Exception as e: + print(f" โŒ TradingAgents SSL configuration failed: {e}") + + # Test 4: Google News functionality + print("\n4๏ธโƒฃ Testing Google News data retrieval:") + + try: + from tradingagents.dataflows.googlenews_utils import getNewsData + from tradingagents.dataflows.ssl_utils import get_ssl_config + from tradingagents.default_config import DEFAULT_CONFIG + + ssl_config = get_ssl_config(DEFAULT_CONFIG) + + # Test news retrieval with SSL config + news_results = getNewsData("AAPL", "2024-01-01", "2024-01-02", ssl_config) + print(f" โœ… Google News retrieval successful, got {len(news_results)} results") + + except Exception as e: + print(f" โŒ Google News retrieval failed: {e}") + +if __name__ == "__main__": + test_api_connections() \ No newline at end of file diff --git a/test_ssl_issues.py b/test_ssl_issues.py new file mode 100644 index 00000000..762c92bc --- /dev/null +++ b/test_ssl_issues.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +""" +Test TradingAgents SSL connections specifically +""" + +import os +import sys +from pathlib import Path + +# Add project root to path +project_root = Path(__file__).parent.absolute() +if str(project_root) not in sys.path: + sys.path.insert(0, str(project_root)) + +from dotenv import load_dotenv +load_dotenv() + +from tradingagents.default_config import DEFAULT_CONFIG +from tradingagents.dataflows.ssl_utils import get_ssl_config, setup_global_ssl_config, get_certificate_info +from tradingagents.graph.trading_graph import TradingAgentsGraph +import requests + +def test_certificate_issues(): + """Test SSL certificate issues that might occur in TradingAgents""" + + print("๐Ÿ” Testing TradingAgents SSL Certificate Issues") + print("=" * 55) + + # Show environment variables + print("\n๐Ÿ“‹ Environment Variables:") + ssl_vars = ["REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE", "SSL_VERIFY", "HTTP_TIMEOUT", + "HTTP_PROXY", "HTTPS_PROXY", "OPENAI_API_KEY", "FINNHUB_API_KEY"] + for var in ssl_vars: + value = os.getenv(var) + if value: + if "API_KEY" in var: + print(f" {var}: {'*' * min(8, len(value))}...") + else: + print(f" {var}: {value}") + else: + print(f" {var}: Not set") + + # Show certificate info + print("\n๐Ÿ“‹ Certificate Bundle Information:") + cert_info = get_certificate_info() + for key, value in cert_info.items(): + if isinstance(value, list): + print(f" {key}: {', '.join(value) if value else 'None found'}") + else: + print(f" {key}: {value}") + + # Test SSL config + print("\nโš™๏ธ Current SSL Configuration:") + config = DEFAULT_CONFIG.copy() + ssl_config = get_ssl_config(config) + print(f" SSL Config: {ssl_config}") + + # Set up global SSL config + print("\n๐Ÿ”ง Setting up global SSL configuration...") + setup_global_ssl_config(config) + + # Test basic HTTPS connections + test_urls = [ + "https://api.openai.com/", + "https://www.google.com/", + "https://openrouter.ai/", + "https://finnhub.io/" + ] + + print(f"\n๐ŸŒ Testing HTTPS connections:") + for url in test_urls: + try: + print(f" Testing {url}...") + response = requests.get(url, timeout=10) + print(f" โœ… {url}: Status {response.status_code}") + except requests.exceptions.SSLError as e: + print(f" โŒ SSL Error for {url}: {e}") + except Exception as e: + print(f" โŒ Connection error for {url}: {e}") + + # Test TradingAgentsGraph initialization + print(f"\n๐Ÿค– Testing TradingAgentsGraph initialization:") + try: + # Create minimal config + test_config = DEFAULT_CONFIG.copy() + test_config["llm_provider"] = "openai" + test_config["quick_think_llm"] = "gpt-3.5-turbo" + test_config["deep_think_llm"] = "gpt-4" + + print(" Creating TradingAgentsGraph...") + graph = TradingAgentsGraph(config=test_config) + print(" โœ… TradingAgentsGraph created successfully") + + # Test if we can make a simple LLM call + print(" Testing LLM connection...") + try: + # This won't actually make an API call but will test the LLM initialization + llm = graph.quick_thinking_llm + print(f" โœ… LLM initialized: {llm}") + except Exception as e: + print(f" โŒ LLM initialization error: {e}") + + except Exception as e: + print(f" โŒ TradingAgentsGraph initialization error: {e}") + import traceback + traceback.print_exc() + + # Recommendations based on findings + print(f"\n๐Ÿ’ก Troubleshooting Recommendations:") + + # Check if we're on macOS and suggest system certificates + if sys.platform == "darwin": + macos_cert = "/etc/ssl/cert.pem" + if os.path.exists(macos_cert): + print(f" ๐Ÿ“ฑ macOS detected - try: export REQUESTS_CA_BUNDLE={macos_cert}") + else: + print(f" ๐Ÿ“ฑ macOS detected but {macos_cert} not found") + + # Check for certifi + try: + import certifi + print(f" ๐Ÿ” Certifi available - try: export REQUESTS_CA_BUNDLE={certifi.where()}") + except ImportError: + print(f" โŒ Certifi not installed - try: pip install certifi") + + # Corporate environment suggestions + print(f" ๐Ÿข If behind corporate firewall:") + print(f" โ€ข Contact IT for corporate certificate bundle") + print(f" โ€ข Check if HTTP_PROXY/HTTPS_PROXY needed") + print(f" โ€ข Ask about custom CA certificates") + + # Temporary workaround (not recommended for production) + print(f" ๐Ÿšจ Temporary workaround (development only):") + print(f" export SSL_VERIFY=false") + print(f" โš ๏ธ This disables SSL verification - use with caution!") + +if __name__ == "__main__": + test_certificate_issues() \ No newline at end of file diff --git a/tradingagents/dataflows/googlenews_utils.py b/tradingagents/dataflows/googlenews_utils.py index bdc6124d..4e775585 100644 --- a/tradingagents/dataflows/googlenews_utils.py +++ b/tradingagents/dataflows/googlenews_utils.py @@ -23,20 +23,36 @@ def is_rate_limited(response): wait=wait_exponential(multiplier=1, min=4, max=60), stop=stop_after_attempt(5), ) -def make_request(url, headers): +def make_request(url, headers, ssl_config=None): """Make a request with retry logic for rate limiting""" # Random delay before each request to avoid detection time.sleep(random.uniform(2, 6)) - response = requests.get(url, headers=headers) + + # Prepare SSL configuration - only use if explicitly configured + kwargs = {} + if ssl_config: + if ssl_config.get("cert_bundle"): + kwargs["verify"] = ssl_config["cert_bundle"] + elif "verify" in ssl_config: + kwargs["verify"] = ssl_config["verify"] + + if ssl_config.get("timeout"): + kwargs["timeout"] = ssl_config["timeout"] + + if ssl_config.get("proxies"): + kwargs["proxies"] = ssl_config["proxies"] + + response = requests.get(url, headers=headers, **kwargs) return response -def getNewsData(query, start_date, end_date): +def getNewsData(query, start_date, end_date, ssl_config=None): """ Scrape Google News search results for a given query and date range. query: str - search query start_date: str - start date in the format yyyy-mm-dd or mm/dd/yyyy end_date: str - end date in the format yyyy-mm-dd or mm/dd/yyyy + ssl_config: dict - SSL configuration including cert_bundle, verify, timeout, proxies """ if "-" in start_date: start_date = datetime.strptime(start_date, "%Y-%m-%d") @@ -64,7 +80,7 @@ def getNewsData(query, start_date, end_date): ) try: - response = make_request(url, headers) + response = make_request(url, headers, ssl_config) soup = BeautifulSoup(response.content, "html.parser") results_on_page = soup.select("div.SoaBEf") diff --git a/tradingagents/dataflows/interface.py b/tradingagents/dataflows/interface.py index 92e91450..4afc8600 100644 --- a/tradingagents/dataflows/interface.py +++ b/tradingagents/dataflows/interface.py @@ -4,6 +4,7 @@ from .yfin_utils import * from .stockstats_utils import * from .googlenews_utils import * from .finnhub_utils import get_data_in_range +from .ssl_utils import get_ssl_config, setup_global_ssl_config from dateutil.relativedelta import relativedelta from concurrent.futures import ThreadPoolExecutor from datetime import datetime @@ -294,7 +295,13 @@ def get_google_news( before = start_date - relativedelta(days=look_back_days) before = before.strftime("%Y-%m-%d") - news_results = getNewsData(query, before, curr_date) + config = get_config() + ssl_config = get_ssl_config(config) + # Only pass ssl_config if it has actual configuration + if ssl_config: + news_results = getNewsData(query, before, curr_date, ssl_config) + else: + news_results = getNewsData(query, before, curr_date) news_str = "" diff --git a/tradingagents/dataflows/ssl_utils.py b/tradingagents/dataflows/ssl_utils.py new file mode 100644 index 00000000..8e9a8177 --- /dev/null +++ b/tradingagents/dataflows/ssl_utils.py @@ -0,0 +1,149 @@ +""" +SSL/TLS configuration utilities for TradingAgents +""" + +import os +import ssl +import certifi +from typing import Dict, Any, Optional + + +def get_ssl_config(config: Dict[str, Any]) -> Dict[str, Any]: + """ + Create SSL configuration dictionary from the main config. + + Args: + config: Main configuration dictionary + + Returns: + SSL configuration dictionary with cert_bundle, verify, timeout, proxies + """ + ssl_config = {} + + # Certificate bundle configuration - only use if explicitly specified + cert_bundle = config.get("ssl_cert_bundle") + if cert_bundle and cert_bundle.strip(): + # Use explicitly specified certificate bundle + ssl_config["cert_bundle"] = cert_bundle + ssl_config["verify"] = cert_bundle + elif not config.get("ssl_verify", True): + # Only disable SSL verification if explicitly set to false + ssl_config["verify"] = False + + # If no explicit cert bundle and ssl_verify is true (default), + # don't set anything - use default behavior + + # Timeout configuration + if config.get("http_timeout"): + ssl_config["timeout"] = config["http_timeout"] + + # Proxy configuration + proxies = {} + if config.get("http_proxy"): + proxies["http"] = config["http_proxy"] + if config.get("https_proxy"): + proxies["https"] = config["https_proxy"] + if proxies: + ssl_config["proxies"] = proxies + + return ssl_config + + +def setup_global_ssl_config(config: Dict[str, Any]) -> None: + """ + Set up global SSL configuration for the application. + This affects all SSL connections made by requests and other libraries. + Only sets configuration if explicitly specified in environment variables. + + Args: + config: Main configuration dictionary + """ + # Set environment variables for requests library only if explicitly configured + cert_bundle = config.get("ssl_cert_bundle") + if cert_bundle and cert_bundle.strip(): + os.environ["REQUESTS_CA_BUNDLE"] = cert_bundle + os.environ["CURL_CA_BUNDLE"] = cert_bundle + print(f"๐Ÿ”’ Using custom SSL certificate bundle: {cert_bundle}") + + # Set SSL verification for requests only if explicitly disabled + if not config.get("ssl_verify", True): + # Disable SSL warnings when verification is disabled + import urllib3 + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + print("โš ๏ธ SSL certificate verification disabled") + + # Set proxy environment variables if specified + if config.get("http_proxy"): + os.environ["HTTP_PROXY"] = config["http_proxy"] + print(f"๐ŸŒ Using HTTP proxy: {config['http_proxy']}") + if config.get("https_proxy"): + os.environ["HTTPS_PROXY"] = config["https_proxy"] + print(f"๐ŸŒ Using HTTPS proxy: {config['https_proxy']}") + + # Set timeout if specified + if config.get("http_timeout"): + print(f"โฑ๏ธ HTTP timeout set to: {config['http_timeout']} seconds") + + +def create_ssl_context(cert_bundle: Optional[str] = None, verify_ssl: bool = True) -> ssl.SSLContext: + """ + Create a custom SSL context with specified certificate bundle. + + Args: + cert_bundle: Path to certificate bundle file + verify_ssl: Whether to verify SSL certificates + + Returns: + Configured SSL context + """ + if not verify_ssl: + # Create unverified context (not recommended for production) + context = ssl._create_unverified_context() + else: + # Create default context + context = ssl.create_default_context() + + if cert_bundle: + # Load custom certificate bundle + context.load_verify_locations(cafile=cert_bundle) + + return context + + +def get_certificate_info() -> Dict[str, str]: + """ + Get information about available certificate bundles. + + Returns: + Dictionary with certificate bundle information + """ + info = {} + + # Check certifi bundle + try: + import certifi + info["certifi_bundle"] = certifi.where() + except ImportError: + info["certifi_bundle"] = "Not available (certifi not installed)" + + # Check environment variables + info["env_ca_bundle"] = os.getenv("REQUESTS_CA_BUNDLE", "Not set") + info["env_curl_bundle"] = os.getenv("CURL_CA_BUNDLE", "Not set") + + # Check system certificate stores + common_cert_paths = [ + "/etc/ssl/certs/ca-certificates.crt", # Debian/Ubuntu + "/etc/pki/tls/certs/ca-bundle.crt", # RedHat/CentOS + "/usr/local/share/certs/ca-root-nss.crt", # FreeBSD + "/etc/ssl/cert.pem", # OpenBSD + "/System/Library/OpenSSL/certs/cert.pem", # macOS + ] + + available_system_certs = [] + for path in common_cert_paths: + if os.path.exists(path): + available_system_certs.append(path) + + info["system_cert_bundles"] = available_system_certs + + return info \ No newline at end of file diff --git a/tradingagents/default_config.py b/tradingagents/default_config.py index 0568f40a..8a4bb06b 100644 --- a/tradingagents/default_config.py +++ b/tradingagents/default_config.py @@ -26,4 +26,11 @@ DEFAULT_CONFIG = { "online_tools": True, "user_position": "none", "cost_per_trade": 0.0, + # SSL/TLS Certificate settings - only use if explicitly set + "ssl_cert_bundle": os.getenv("REQUESTS_CA_BUNDLE") or os.getenv("CURL_CA_BUNDLE"), + "ssl_verify": os.getenv("SSL_VERIFY", "true").lower() in ("true", "1", "yes"), + "http_timeout": int(os.getenv("HTTP_TIMEOUT")) if os.getenv("HTTP_TIMEOUT") else None, + # Proxy settings (if needed) + "http_proxy": os.getenv("HTTP_PROXY"), + "https_proxy": os.getenv("HTTPS_PROXY"), } diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index 4d86d74b..03508cc7 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -21,6 +21,7 @@ from tradingagents.agents.utils.agent_states import ( RiskDebateState, ) from tradingagents.dataflows.interface import set_config +from tradingagents.dataflows.ssl_utils import setup_global_ssl_config from .conditional_logic import ConditionalLogic from .setup import GraphSetup @@ -50,6 +51,9 @@ class TradingAgentsGraph: # Update the interface's config set_config(self.config) + + # Set up global SSL configuration + setup_global_ssl_config(self.config) # Create necessary directories os.makedirs( @@ -79,15 +83,48 @@ class TradingAgentsGraph: "export OPENAI_API_KEY=your_openai_key_here" ) + # Prepare SSL configuration for HTTP client - only if explicitly configured + http_client_kwargs = {} + cert_bundle = self.config.get("ssl_cert_bundle") + + if cert_bundle and cert_bundle.strip(): + import httpx + http_client_kwargs["verify"] = cert_bundle + elif not self.config.get("ssl_verify", True): + import httpx + http_client_kwargs["verify"] = False + + if self.config.get("http_timeout"): + import httpx + http_client_kwargs["timeout"] = self.config["http_timeout"] + + # Add proxy configuration if specified + if self.config.get("http_proxy") or self.config.get("https_proxy"): + import httpx + proxies = {} + if self.config.get("http_proxy"): + proxies["http://"] = self.config["http_proxy"] + if self.config.get("https_proxy"): + proxies["https://"] = self.config["https_proxy"] + http_client_kwargs["proxies"] = proxies + + # Create HTTP client only if we have custom settings + http_client = None + if http_client_kwargs: + import httpx + http_client = httpx.Client(**http_client_kwargs) + self.deep_thinking_llm = ChatOpenAI( model=self.config["deep_think_llm"], base_url=self.config["backend_url"], - api_key=api_key + api_key=api_key, + http_client=http_client ) self.quick_thinking_llm = ChatOpenAI( model=self.config["quick_think_llm"], base_url=self.config["backend_url"], - api_key=api_key + api_key=api_key, + http_client=http_client ) elif self.config["llm_provider"].lower() == "anthropic": self.deep_thinking_llm = ChatAnthropic(model=self.config["deep_think_llm"], base_url=self.config["backend_url"])