745 lines
23 KiB
Python
Executable File
745 lines
23 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
TradingAgents Web Interface
|
|
|
|
A beautiful web UI for running TradingAgents analysis and managing trades.
|
|
|
|
Usage:
|
|
chainlit run web_app.py -w
|
|
|
|
Then open http://localhost:8000 in your browser!
|
|
"""
|
|
|
|
import chainlit as cl
|
|
import logging
|
|
from decimal import Decimal, InvalidOperation
|
|
from datetime import datetime
|
|
import json
|
|
from typing import Optional
|
|
|
|
from tradingagents.graph.trading_graph import TradingAgentsGraph
|
|
from tradingagents.default_config import DEFAULT_CONFIG
|
|
from tradingagents.brokers import AlpacaBroker
|
|
from tradingagents.brokers.base import OrderSide, OrderType
|
|
from tradingagents.security import validate_ticker
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@cl.on_chat_start
|
|
async def start() -> None:
|
|
"""
|
|
Initialize the chat session and welcome the user.
|
|
|
|
Sets up session state, initializes configuration, and displays
|
|
a welcome message with available commands.
|
|
|
|
Session Variables:
|
|
- ta_graph: TradingAgentsGraph instance (lazily initialized)
|
|
- broker: AlpacaBroker instance (lazily initialized)
|
|
- config: Configuration dictionary
|
|
- broker_connected: Boolean connection status
|
|
|
|
Note:
|
|
All state is stored in Chainlit's user_session to avoid
|
|
global variables and enable multi-user support.
|
|
"""
|
|
logger.info("Chat session started - initializing session state")
|
|
# Initialize session state - NO GLOBAL VARIABLES
|
|
cl.user_session.set("ta_graph", None)
|
|
cl.user_session.set("broker", None)
|
|
cl.user_session.set("config", DEFAULT_CONFIG.copy())
|
|
cl.user_session.set("broker_connected", False)
|
|
|
|
logger.debug("Session state initialized")
|
|
|
|
await cl.Message(
|
|
content="""# 🤖 Welcome to TradingAgents!
|
|
|
|
I'm your AI-powered trading assistant. I can help you:
|
|
|
|
📊 **Analyze Stocks** - Deep analysis using multiple expert agents
|
|
💼 **Manage Positions** - Track your portfolio and P&L
|
|
📈 **Execute Trades** - Paper trading integration with Alpaca
|
|
📉 **View Reports** - Detailed analysis and recommendations
|
|
|
|
**Quick Commands:**
|
|
- `analyze AAPL` - Analyze a stock
|
|
- `portfolio` - View current positions
|
|
- `account` - Check account status
|
|
- `help` - Show all commands
|
|
|
|
**Getting Started:**
|
|
1. Make sure your `.env` is configured
|
|
2. Try analyzing a stock: `analyze NVDA`
|
|
3. Review the detailed analysis
|
|
4. Execute trades based on signals!
|
|
|
|
What would you like to do?
|
|
"""
|
|
).send()
|
|
|
|
|
|
@cl.on_message
|
|
async def main(message: cl.Message) -> None:
|
|
"""
|
|
Handle incoming chat messages and dispatch to appropriate handlers.
|
|
|
|
Parses user input, validates commands, and routes to the corresponding
|
|
async handler function.
|
|
|
|
Args:
|
|
message: Chainlit Message object containing user input
|
|
|
|
Supported Commands:
|
|
- help: Display available commands
|
|
- analyze TICKER: Analyze stock
|
|
- portfolio: View positions
|
|
- account: View account status
|
|
- connect: Connect to paper trading
|
|
- buy TICKER QTY: Buy shares
|
|
- sell TICKER QTY: Sell shares
|
|
- settings: View settings
|
|
- provider NAME: Change LLM provider
|
|
|
|
Note:
|
|
All input is validated to prevent command injection and
|
|
other security issues.
|
|
"""
|
|
msg_content = message.content.strip().lower()
|
|
parts = msg_content.split()
|
|
|
|
logger.debug("Received message: %s", msg_content)
|
|
|
|
if not parts:
|
|
await cl.Message(content="Please enter a command. Type `help` for options.").send()
|
|
return
|
|
|
|
command = parts[0]
|
|
logger.info("Processing command: %s", command)
|
|
|
|
# Help command
|
|
if command == "help":
|
|
await show_help()
|
|
|
|
# Analyze command
|
|
elif command == "analyze":
|
|
if len(parts) < 2:
|
|
await cl.Message(content="Usage: `analyze TICKER`\n\nExample: `analyze AAPL`").send()
|
|
return
|
|
|
|
# SECURITY: Validate ticker to prevent command injection
|
|
try:
|
|
ticker = validate_ticker(parts[1])
|
|
logger.info("User requested analysis for ticker: %s", ticker)
|
|
await analyze_stock(ticker)
|
|
except ValueError as e:
|
|
logger.warning("Invalid ticker input: %s", str(e))
|
|
await cl.Message(content=f"❌ Invalid ticker: {e}").send()
|
|
return
|
|
|
|
# Portfolio command
|
|
elif command == "portfolio":
|
|
await show_portfolio()
|
|
|
|
# Account command
|
|
elif command == "account":
|
|
await show_account()
|
|
|
|
# Connect broker command
|
|
elif command == "connect":
|
|
await connect_broker()
|
|
|
|
# Buy command
|
|
elif command == "buy":
|
|
if len(parts) < 3:
|
|
await cl.Message(content="Usage: `buy TICKER QUANTITY`\n\nExample: `buy AAPL 10`").send()
|
|
return
|
|
|
|
# SECURITY: Validate ticker to prevent command injection
|
|
try:
|
|
ticker = validate_ticker(parts[1])
|
|
except ValueError as e:
|
|
await cl.Message(content=f"❌ Invalid ticker: {e}").send()
|
|
return
|
|
|
|
# SECURITY: Validate quantity
|
|
try:
|
|
quantity = Decimal(parts[2])
|
|
if quantity <= 0:
|
|
raise ValueError("Quantity must be positive")
|
|
if quantity > Decimal('100000'):
|
|
raise ValueError("Quantity too large (max 100,000 shares)")
|
|
await execute_buy(ticker, quantity)
|
|
except (ValueError, InvalidOperation) as e:
|
|
await cl.Message(content=f"❌ Invalid quantity: {e}").send()
|
|
|
|
# Sell command
|
|
elif command == "sell":
|
|
if len(parts) < 3:
|
|
await cl.Message(content="Usage: `sell TICKER QUANTITY`\n\nExample: `sell AAPL 10`").send()
|
|
return
|
|
|
|
# SECURITY: Validate ticker to prevent command injection
|
|
try:
|
|
ticker = validate_ticker(parts[1])
|
|
except ValueError as e:
|
|
await cl.Message(content=f"❌ Invalid ticker: {e}").send()
|
|
return
|
|
|
|
# SECURITY: Validate quantity
|
|
try:
|
|
quantity = Decimal(parts[2])
|
|
if quantity <= 0:
|
|
raise ValueError("Quantity must be positive")
|
|
if quantity > Decimal('100000'):
|
|
raise ValueError("Quantity too large (max 100,000 shares)")
|
|
await execute_sell(ticker, quantity)
|
|
except (ValueError, InvalidOperation) as e:
|
|
await cl.Message(content=f"❌ Invalid quantity: {e}").send()
|
|
|
|
# Settings command
|
|
elif command == "settings":
|
|
await show_settings()
|
|
|
|
# Set LLM provider
|
|
elif command == "provider":
|
|
if len(parts) < 2:
|
|
await cl.Message(content="Usage: `provider PROVIDER`\n\nOptions: openai, anthropic, google").send()
|
|
return
|
|
|
|
provider = parts[1].lower()
|
|
await set_provider(provider)
|
|
|
|
else:
|
|
await cl.Message(
|
|
content=f"Unknown command: `{command}`\n\nType `help` to see available commands."
|
|
).send()
|
|
|
|
|
|
async def show_help() -> None:
|
|
"""
|
|
Display help message with all available commands and examples.
|
|
|
|
Shows user all supported commands, their syntax, and usage examples.
|
|
"""
|
|
logger.debug("Displaying help message")
|
|
await cl.Message(
|
|
content="""# 📚 TradingAgents Commands
|
|
|
|
## Analysis
|
|
- `analyze TICKER` - Analyze a stock with all agents
|
|
- `settings` - View current settings
|
|
- `provider NAME` - Change LLM provider (openai/anthropic/google)
|
|
|
|
## Trading
|
|
- `connect` - Connect to paper trading broker
|
|
- `account` - View account balance and buying power
|
|
- `portfolio` - View all positions and P&L
|
|
- `buy TICKER QTY` - Buy shares (e.g., `buy AAPL 10`)
|
|
- `sell TICKER QTY` - Sell shares (e.g., `sell AAPL 10`)
|
|
|
|
## Examples
|
|
```
|
|
analyze NVDA
|
|
buy NVDA 5
|
|
portfolio
|
|
sell NVDA 5
|
|
```
|
|
|
|
**Tips:**
|
|
- Start with `analyze` to get AI insights
|
|
- Use `connect` to enable paper trading
|
|
- Check `portfolio` regularly to track P&L
|
|
- All trades are paper trading (no real money!)
|
|
"""
|
|
).send()
|
|
|
|
|
|
async def analyze_stock(ticker: str) -> None:
|
|
"""
|
|
Analyze a stock using TradingAgents multi-agent system.
|
|
|
|
Runs market, fundamentals, and news analysis on the specified ticker.
|
|
Uses multiple expert agents to provide comprehensive analysis and
|
|
trading signals.
|
|
|
|
Args:
|
|
ticker: Stock ticker symbol (e.g., "AAPL", "NVDA")
|
|
|
|
Analysis Includes:
|
|
- Market analysis: Technical indicators and price action
|
|
- Fundamentals analysis: P/E, earnings, growth metrics
|
|
- News sentiment: Recent news sentiment analysis
|
|
- Investment decision: Combined recommendation
|
|
|
|
Performance:
|
|
Typical analysis time: 1-2 minutes (network and LLM dependent)
|
|
|
|
Note:
|
|
Analysis results are stored in session for reference during
|
|
subsequent trading operations.
|
|
"""
|
|
# Get from session instead of global
|
|
ta_graph = cl.user_session.get("ta_graph")
|
|
|
|
logger.info("Starting analysis for ticker: %s", ticker)
|
|
|
|
# Show loading message
|
|
msg = cl.Message(content=f"🔍 Analyzing **{ticker}** with TradingAgents...\n\nThis may take 1-2 minutes...")
|
|
await msg.send()
|
|
|
|
try:
|
|
# Initialize TradingAgents if needed
|
|
if ta_graph is None:
|
|
logger.debug("Initializing TradingAgentsGraph for first time")
|
|
config = cl.user_session.get("config")
|
|
ta_graph = TradingAgentsGraph(
|
|
selected_analysts=["market", "fundamentals", "news"],
|
|
config=config
|
|
)
|
|
# Store in session
|
|
cl.user_session.set("ta_graph", ta_graph)
|
|
|
|
# Run analysis
|
|
trade_date = datetime.now().strftime("%Y-%m-%d")
|
|
logger.debug("Running analysis for %s on %s", ticker, trade_date)
|
|
final_state, signal = ta_graph.propagate(ticker, trade_date)
|
|
logger.info("Analysis completed for %s: signal=%s", ticker, signal)
|
|
|
|
# Format results
|
|
result = f"""# 📊 Analysis Complete: {ticker}
|
|
|
|
## 🎯 Trading Signal: **{signal}**
|
|
|
|
### Market Analysis
|
|
{final_state.get('market_report', 'No market data available')[:500]}...
|
|
|
|
### Fundamentals Analysis
|
|
{final_state.get('fundamentals_report', 'No fundamentals data available')[:500]}...
|
|
|
|
### News Sentiment
|
|
{final_state.get('news_report', 'No news data available')[:500]}...
|
|
|
|
### Investment Decision
|
|
{final_state.get('trader_investment_plan', 'No decision available')[:500]}...
|
|
|
|
---
|
|
|
|
**Recommendation:** {signal}
|
|
|
|
Would you like to execute this signal? Use:
|
|
- `buy {ticker} <quantity>` if signal is BUY
|
|
- `sell {ticker} <quantity>` if signal is SELL
|
|
"""
|
|
|
|
await cl.Message(content=result).send()
|
|
|
|
# Store analysis in session
|
|
cl.user_session.set("last_analysis", {
|
|
"ticker": ticker,
|
|
"signal": signal,
|
|
"state": final_state
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error("Analysis failed for %s: %s", ticker, str(e), exc_info=True)
|
|
await cl.Message(
|
|
content=f"❌ Analysis failed: {str(e)}\n\nThis might be due to:\n- API quota limits\n- Network issues\n- Invalid ticker\n\nPlease try again or check your configuration."
|
|
).send()
|
|
|
|
|
|
async def connect_broker() -> None:
|
|
"""
|
|
Connect to Alpaca paper trading broker.
|
|
|
|
Establishes connection to Alpaca paper trading account and verifies
|
|
credentials. Displays account information upon successful connection.
|
|
|
|
The broker instance and connection state are stored in the Chainlit
|
|
session for use by subsequent trading operations.
|
|
|
|
Requires Environment Variables:
|
|
- ALPACA_API_KEY: Alpaca API key
|
|
- ALPACA_SECRET_KEY: Alpaca secret key
|
|
|
|
Example Output:
|
|
Shows account number, cash balance, buying power, and portfolio value.
|
|
|
|
Note:
|
|
Paper trading is a simulated trading environment for testing
|
|
without real capital.
|
|
"""
|
|
logger.info("User requested broker connection")
|
|
|
|
if cl.user_session.get("broker_connected"):
|
|
logger.debug("Broker already connected, skipping connection attempt")
|
|
await cl.Message(content="✓ Already connected to Alpaca paper trading!").send()
|
|
return
|
|
|
|
msg = cl.Message(content="🔌 Connecting to Alpaca paper trading...")
|
|
await msg.send()
|
|
|
|
try:
|
|
logger.debug("Creating AlpacaBroker instance")
|
|
broker = AlpacaBroker(paper_trading=True)
|
|
broker.connect()
|
|
|
|
logger.debug("Fetching account information")
|
|
account = broker.get_account()
|
|
|
|
# Store in session
|
|
cl.user_session.set("broker", broker)
|
|
cl.user_session.set("broker_connected", True)
|
|
|
|
logger.info("Successfully connected to Alpaca (Account: %s)", account.account_number)
|
|
|
|
await cl.Message(
|
|
content=f"""✓ Connected to Alpaca Paper Trading!
|
|
|
|
**Account:** {account.account_number}
|
|
**Cash:** ${account.cash:,.2f}
|
|
**Buying Power:** ${account.buying_power:,.2f}
|
|
**Portfolio Value:** ${account.portfolio_value:,.2f}
|
|
|
|
You can now execute trades!
|
|
"""
|
|
).send()
|
|
|
|
except Exception as e:
|
|
logger.error("Broker connection failed: %s", str(e), exc_info=True)
|
|
await cl.Message(
|
|
content=f"""❌ Connection failed: {str(e)}
|
|
|
|
**Setup Required:**
|
|
1. Sign up at https://alpaca.markets
|
|
2. Get your API keys
|
|
3. Add to `.env`:
|
|
```
|
|
ALPACA_API_KEY=your_key
|
|
ALPACA_SECRET_KEY=your_secret
|
|
ALPACA_PAPER_TRADING=true
|
|
```
|
|
4. Restart the app
|
|
"""
|
|
).send()
|
|
|
|
|
|
async def show_account() -> None:
|
|
"""
|
|
Display current account status and financial metrics.
|
|
|
|
Shows cash balance, buying power, portfolio value, and P&L information.
|
|
Requires active broker connection.
|
|
|
|
Displayed Information:
|
|
- Account number
|
|
- Available cash
|
|
- Buying power (margin available)
|
|
- Current portfolio value
|
|
- Total equity
|
|
- Session P&L (profit/loss)
|
|
|
|
Requires:
|
|
- Broker connection via `connect` command first
|
|
"""
|
|
logger.debug("User requested account status")
|
|
broker = cl.user_session.get("broker")
|
|
|
|
if not broker or not cl.user_session.get("broker_connected"):
|
|
logger.warning("Account requested but broker not connected")
|
|
await cl.Message(content="⚠️ Not connected. Use `connect` first!").send()
|
|
return
|
|
|
|
try:
|
|
logger.debug("Fetching account information")
|
|
account = broker.get_account()
|
|
|
|
session_pnl = account.equity - account.last_equity
|
|
logger.debug("Account data retrieved: cash=%.2f, bp=%.2f, pnl=%.2f",
|
|
account.cash, account.buying_power, session_pnl)
|
|
|
|
await cl.Message(
|
|
content=f"""# 💰 Account Status
|
|
|
|
**Account Number:** {account.account_number}
|
|
**Cash Available:** ${account.cash:,.2f}
|
|
**Buying Power:** ${account.buying_power:,.2f}
|
|
**Portfolio Value:** ${account.portfolio_value:,.2f}
|
|
**Total Equity:** ${account.equity:,.2f}
|
|
|
|
**Session P&L:** ${session_pnl:,.2f}
|
|
|
|
Type `portfolio` to see your positions.
|
|
"""
|
|
).send()
|
|
|
|
except Exception as e:
|
|
logger.error("Failed to fetch account: %s", str(e), exc_info=True)
|
|
await cl.Message(content=f"❌ Error: {str(e)}").send()
|
|
|
|
|
|
async def show_portfolio() -> None:
|
|
"""
|
|
Display all current positions and portfolio metrics.
|
|
|
|
Shows all open positions with quantity, entry price, current price,
|
|
market value, and unrealized P&L for each position.
|
|
|
|
Displayed Information per Position:
|
|
- Ticker symbol
|
|
- Quantity held
|
|
- Average entry price
|
|
- Current market price
|
|
- Current market value
|
|
- Unrealized profit/loss (dollars and percentage)
|
|
|
|
Summary Totals:
|
|
- Total position value across all holdings
|
|
- Total unrealized P&L across portfolio
|
|
|
|
Requires:
|
|
- Broker connection via `connect` command first
|
|
"""
|
|
logger.debug("User requested portfolio view")
|
|
broker = cl.user_session.get("broker")
|
|
|
|
if not broker or not cl.user_session.get("broker_connected"):
|
|
logger.warning("Portfolio requested but broker not connected")
|
|
await cl.Message(content="⚠️ Not connected. Use `connect` first!").send()
|
|
return
|
|
|
|
try:
|
|
logger.debug("Fetching positions")
|
|
positions = broker.get_positions()
|
|
|
|
if not positions:
|
|
logger.debug("No positions found")
|
|
await cl.Message(content="📭 No positions currently held.").send()
|
|
return
|
|
|
|
result = "# 📈 Current Positions\n\n"
|
|
total_value = Decimal("0")
|
|
total_pnl = Decimal("0")
|
|
|
|
for pos in positions:
|
|
result += f"""## {pos.symbol}
|
|
- **Quantity:** {pos.quantity} shares
|
|
- **Avg Cost:** ${pos.avg_entry_price:.2f}
|
|
- **Current Price:** ${pos.current_price:.2f}
|
|
- **Market Value:** ${pos.market_value:,.2f}
|
|
- **P&L:** ${pos.unrealized_pnl:,.2f} ({pos.unrealized_pnl_percent:.2%})
|
|
|
|
"""
|
|
total_value += pos.market_value
|
|
total_pnl += pos.unrealized_pnl
|
|
|
|
result += f"""---
|
|
**Total Position Value:** ${total_value:,.2f}
|
|
**Total Unrealized P&L:** ${total_pnl:,.2f}
|
|
"""
|
|
|
|
logger.debug("Portfolio retrieved: %d positions, total_value=%.2f, total_pnl=%.2f",
|
|
len(positions), total_value, total_pnl)
|
|
|
|
await cl.Message(content=result).send()
|
|
|
|
except Exception as e:
|
|
logger.error("Failed to fetch portfolio: %s", str(e), exc_info=True)
|
|
await cl.Message(content=f"❌ Error: {str(e)}").send()
|
|
|
|
|
|
async def execute_buy(ticker: str, quantity: Decimal) -> None:
|
|
"""
|
|
Execute a market buy order for the specified ticker and quantity.
|
|
|
|
Places a market buy order at the current market price. Requires
|
|
sufficient buying power in the account.
|
|
|
|
Args:
|
|
ticker: Stock ticker symbol (e.g., "AAPL")
|
|
quantity: Number of shares to buy (Decimal)
|
|
|
|
Error Handling:
|
|
- Validates sufficient buying power
|
|
- Handles connection errors
|
|
- Returns detailed error messages
|
|
|
|
Note:
|
|
- Uses market order (executes at current market price)
|
|
- Order status can be checked via `portfolio` command
|
|
- Actual fill price may differ from market price
|
|
|
|
Requires:
|
|
- Broker connection via `connect` command first
|
|
"""
|
|
logger.info("User requested buy order: %s qty=%s", ticker, quantity)
|
|
broker = cl.user_session.get("broker")
|
|
|
|
if not broker or not cl.user_session.get("broker_connected"):
|
|
logger.warning("Buy requested but broker not connected")
|
|
await cl.Message(content="⚠️ Not connected. Use `connect` first!").send()
|
|
return
|
|
|
|
msg = cl.Message(content=f"🔄 Placing buy order for {quantity} shares of {ticker}...")
|
|
await msg.send()
|
|
|
|
try:
|
|
logger.debug("Executing buy order: %s qty=%s", ticker, quantity)
|
|
order = broker.buy_market(ticker, quantity)
|
|
|
|
logger.info("Buy order placed successfully: %s qty=%s order_id=%s",
|
|
ticker, quantity, order.order_id)
|
|
|
|
await cl.Message(
|
|
content=f"""✓ Buy order placed successfully!
|
|
|
|
**Order ID:** {order.order_id}
|
|
**Symbol:** {order.symbol}
|
|
**Quantity:** {order.quantity}
|
|
**Status:** {order.status.value}
|
|
|
|
Check your `portfolio` to see the position.
|
|
"""
|
|
).send()
|
|
|
|
except Exception as e:
|
|
logger.error("Buy order failed for %s: %s", ticker, str(e), exc_info=True)
|
|
await cl.Message(content=f"❌ Order failed: {str(e)}").send()
|
|
|
|
|
|
async def execute_sell(ticker: str, quantity: Decimal) -> None:
|
|
"""
|
|
Execute a market sell order for the specified ticker and quantity.
|
|
|
|
Places a market sell order at the current market price. The account
|
|
must hold at least the specified quantity of the stock.
|
|
|
|
Args:
|
|
ticker: Stock ticker symbol (e.g., "AAPL")
|
|
quantity: Number of shares to sell (Decimal)
|
|
|
|
Error Handling:
|
|
- Validates position exists and has sufficient quantity
|
|
- Handles connection errors
|
|
- Returns detailed error messages
|
|
|
|
Note:
|
|
- Uses market order (executes at current market price)
|
|
- Position is closed or reduced based on quantity sold
|
|
- Actual fill price may differ from market price
|
|
- Proceeds are added to cash balance
|
|
|
|
Requires:
|
|
- Broker connection via `connect` command first
|
|
"""
|
|
logger.info("User requested sell order: %s qty=%s", ticker, quantity)
|
|
broker = cl.user_session.get("broker")
|
|
|
|
if not broker or not cl.user_session.get("broker_connected"):
|
|
logger.warning("Sell requested but broker not connected")
|
|
await cl.Message(content="⚠️ Not connected. Use `connect` first!").send()
|
|
return
|
|
|
|
msg = cl.Message(content=f"🔄 Placing sell order for {quantity} shares of {ticker}...")
|
|
await msg.send()
|
|
|
|
try:
|
|
logger.debug("Executing sell order: %s qty=%s", ticker, quantity)
|
|
order = broker.sell_market(ticker, quantity)
|
|
|
|
logger.info("Sell order placed successfully: %s qty=%s order_id=%s",
|
|
ticker, quantity, order.order_id)
|
|
|
|
await cl.Message(
|
|
content=f"""✓ Sell order placed successfully!
|
|
|
|
**Order ID:** {order.order_id}
|
|
**Symbol:** {order.symbol}
|
|
**Quantity:** {order.quantity}
|
|
**Status:** {order.status.value}
|
|
|
|
Check your `portfolio` to see updated positions.
|
|
"""
|
|
).send()
|
|
|
|
except Exception as e:
|
|
logger.error("Sell order failed for %s: %s", ticker, str(e), exc_info=True)
|
|
await cl.Message(content=f"❌ Order failed: {str(e)}").send()
|
|
|
|
|
|
async def show_settings() -> None:
|
|
"""
|
|
Display current application settings and configuration.
|
|
|
|
Shows the configured LLM provider, models, and connection status.
|
|
Provides information about how to change settings.
|
|
|
|
Displayed Settings:
|
|
- LLM Provider (openai, anthropic, google)
|
|
- Deep thinking model for analysis
|
|
- Quick thinking model for simple tasks
|
|
- Broker connection status
|
|
"""
|
|
logger.debug("User requested settings view")
|
|
config = cl.user_session.get("config")
|
|
broker_connected = cl.user_session.get('broker_connected', False)
|
|
|
|
logger.debug("Settings: provider=%s, broker_connected=%s",
|
|
config.get('llm_provider', 'openai'), broker_connected)
|
|
|
|
await cl.Message(
|
|
content=f"""# ⚙️ Current Settings
|
|
|
|
**LLM Provider:** {config.get('llm_provider', 'openai')}
|
|
**Deep Think Model:** {config.get('deep_think_llm', 'gpt-4o')}
|
|
**Quick Think Model:** {config.get('quick_think_llm', 'gpt-4o-mini')}
|
|
**Broker Connected:** {broker_connected}
|
|
|
|
To change LLM provider, use: `provider NAME`
|
|
|
|
Available providers: openai, anthropic, google
|
|
"""
|
|
).send()
|
|
|
|
|
|
async def set_provider(provider: str) -> None:
|
|
"""
|
|
Change the LLM provider for analysis operations.
|
|
|
|
Updates the session configuration to use the specified LLM provider.
|
|
Resets the TradingAgents graph to use the new provider for subsequent
|
|
analysis requests.
|
|
|
|
Args:
|
|
provider: LLM provider name (openai, anthropic, or google)
|
|
|
|
Supported Providers:
|
|
- openai: GPT-4, GPT-4O
|
|
- anthropic: Claude models
|
|
- google: Gemini models
|
|
|
|
Note:
|
|
The provider change takes effect on the next analysis command.
|
|
"""
|
|
logger.info("User requested provider change: %s", provider)
|
|
|
|
if provider not in ["openai", "anthropic", "google"]:
|
|
logger.warning("Invalid provider requested: %s", provider)
|
|
await cl.Message(content="❌ Invalid provider. Choose: openai, anthropic, or google").send()
|
|
return
|
|
|
|
config = cl.user_session.get("config")
|
|
config["llm_provider"] = provider
|
|
|
|
# Reset TradingAgents to use new provider
|
|
cl.user_session.set("ta_graph", None)
|
|
|
|
logger.debug("Provider set to: %s, TradingAgentsGraph reset", provider)
|
|
|
|
await cl.Message(content=f"✓ LLM provider set to **{provider}**\n\nNext analysis will use this provider.").send()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("Run with: chainlit run web_app.py -w")
|