import React, { useState, useEffect, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Users, User, BarChart2, AlertCircle, PlayCircle, Loader2 } from 'lucide-react'; import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; import { HumanMessage, SystemMessage } from '@langchain/core/messages'; const agents = { macro: { name: 'Macro Economist', role: 'Analyzes broader economic trends, rates, and inflation', icon: , color: 'text-blue-500', bg: 'bg-blue-500/10' }, tech: { name: 'Technical Analyst', role: 'Focuses on momentum, moving averages, and charts', icon: , color: 'text-purple-500', bg: 'bg-purple-500/10' }, skeptic: { name: 'The Skeptic', role: 'Looks for flaws, overvaluation, and risks', icon: , color: 'text-red-500', bg: 'bg-red-500/10' }, system: { name: 'System', role: 'Moderator', icon: , color: 'text-gs-gold', bg: 'bg-gs-gold/10' } }; const mockDebates = { 'AAPL': [ { agent: 'macro', text: 'iPhone demand remains steady globally, but services growth is the real story for Apple.' }, { agent: 'tech', text: 'AAPL is consolidating near its 200-day average. A breakout above current levels would be very bullish.' }, { agent: 'skeptic', text: 'Regulatory pressure in the EU and US is a massive dark cloud that could hurt margins.' }, { agent: 'system', text: 'Consensus: Strong ecosystem but legal risks. Conviction Score: 72/100 (Hold).' } ], 'TSLA': [ { agent: 'macro', text: 'High interest rates are cooling the EV market, forcing aggressive price cuts.' }, { agent: 'tech', text: 'Tesla is in a clear downtrend. We need to see a higher low before turning bullish.' }, { agent: 'skeptic', text: 'Elon is distracted and competition from China is fierce. Valuation is still disconnected from reality.' }, { agent: 'system', text: 'Consensus: High volatility and macro headwinds. Conviction Score: 35/100 (Underweight).' } ], 'NVDA': [ { agent: 'macro', text: 'The AI infrastructure build-out is a once-in-a-generation shift that favors NVIDIA.' }, { agent: 'tech', text: 'Parabolic move. It is overextended, but momentum like this can last longer than expected.' }, { agent: 'skeptic', text: 'At some point, the hyperscalers will stop buying at this rate. The drop will be as fast as the rise.' }, { agent: 'system', text: 'Consensus: Unrivaled leader in a booming sector. Conviction Score: 88/100 (Overweight).' } ], 'MSFT': [ { agent: 'macro', text: 'Enterprise software and cloud (Azure) are the backbone of the modern economy.' }, { agent: 'tech', text: 'Steady uptrend. Microsoft is a "safe haven" in the tech world right now.' }, { agent: 'skeptic', text: 'The Activision deal is done, but integrating it perfectly won\'t be easy or cheap.' }, { agent: 'system', text: 'Consensus: Solid growth and AI tailwinds. Conviction Score: 82/100 (Overweight).' } ], 'default': [ { agent: 'macro', text: 'The macro outlook for [TICKER] is heavily dependent on current sector trends and inflationary pressures affecting production costs.' }, { agent: 'tech', text: '[TICKER] is currently testing a key resistance level. We need to see sustained volume before confirming a new upward trajectory.' }, { agent: 'skeptic', text: 'A primary concern for [TICKER] is the potential for margin compression if competitors maintain aggressive pricing strategies.' }, { agent: 'system', text: 'Consensus: Position is stable for [TICKER], but suggests a cautious stance until quarterly data confirms growth. Conviction Score: 62/100 (Neutral).' } ] }; export default function InvestmentCommittee({ ticker, isDebating: externalIsDebating, setIsDebating: externalSetIsDebating, inline = false }) { const [messages, setMessages] = useState([]); const [convictionScore, setConvictionScore] = useState(null); const [loading, setLoading] = useState(false); const [internalIsDebating, setInternalIsDebating] = useState(false); const scrollRef = useRef(null); const isDebating = externalIsDebating !== undefined ? externalIsDebating : internalIsDebating; const setIsDebating = externalSetIsDebating !== undefined ? externalSetIsDebating : setInternalIsDebating; useEffect(() => { setMessages([]); setConvictionScore(null); setIsDebating(false); }, [ticker]); useEffect(() => { if (scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }, [messages]); const runDebate = async () => { if (!ticker) return; setIsDebating(true); setMessages([]); setConvictionScore(null); setLoading(true); const apiKey = import.meta.env.VITE_GEMINI_API_KEY; if (apiKey && apiKey.length > 10) { try { const llm = new ChatGoogleGenerativeAI({ apiKey: apiKey, modelName: 'gemini-1.5-flash', maxOutputTokens: 2048, }); // Agent 1: Macro setMessages([{ agent: 'macro', text: `Quantifying macro factors for ${ticker}...` }]); const macroResponse = await llm.invoke([ new SystemMessage(`You are a top-tier Macro Economist. Analyze the stock ${ticker} with extreme specificity. Mention a specific economic factor like "global logistics", "energy costs", or "labor markets" as it relates to this EXACT company. Provide a 12-month price target. 1 short sentence.`), new HumanMessage(`What is your unique macro view on ${ticker}?`) ]); setMessages([{ agent: 'macro', text: macroResponse.content }]); // Agent 2: Tech setMessages(prev => [...prev, { agent: 'tech', text: `Evaluating technical metrics for ${ticker}...` }]); const techResponse = await llm.invoke([ new SystemMessage(`You are a Technical Analyst. Analyze ${ticker}'s price chart. Mention a specific "support zone", "RSI divergence", or "moving average cross" observation for this company. Provide a specific Fair Value. 1 sentence.`), new HumanMessage(`Provide a non-generic technical view on ${ticker}.`) ]); setMessages(prev => [prev[0], { agent: 'tech', text: techResponse.content }]); // Agent 3: Skeptic setMessages(prev => [...prev, { agent: 'skeptic', text: `Quantifying downside risks for ${ticker}...` }]); const skepticResponse = await llm.invoke([ new SystemMessage(`You are a Professional Skeptic. Find a "poison pill" for ${ticker}. What is the one specific, non-obvious risk (e.g. patent cliff, specific litigation, supply chain bottleneck) for this stock? 1 sentence.`), new HumanMessage(`What is the hidden risk in ${ticker}?`) ]); setMessages(prev => [prev[0], prev[1], { agent: 'skeptic', text: skepticResponse.content }]); // System consensus setMessages(prev => [...prev, { agent: 'system', text: 'Synthesizing quantitative consensus...' }]); const consensusResponse = await llm.invoke([ new SystemMessage("Committee Moderator. Based on the previous data points, output a final Conviction Score (0-100). Format: [SCORE] followed by a 1-sentence quantitative justification."), new HumanMessage(`Synthesize these quantitative views for ${ticker}: 1. ${macroResponse.content} 2. ${techResponse.content} 3. ${skepticResponse.content}`) ]); let score = parseInt(consensusResponse.content.replace(/\D/g,'')); if (isNaN(score)) score = 50; setMessages(prev => [prev[0], prev[1], prev[2], { agent: 'system', text: consensusResponse.content }]); setConvictionScore(score); } catch (error) { console.error("LLM Error:", error); runMockDebate(); } } else { runMockDebate(); } setLoading(false); }; const runMockDebate = () => { const script = mockDebates[ticker] || mockDebates['default']; const debateScript = script.map(msg => ({ ...msg, text: msg.text.replace(/\[TICKER\]/g, ticker) })); let step = 0; const interval = setInterval(() => { if (step < debateScript.length) { const msg = debateScript[step]; if (msg.agent === 'system' && msg.text.includes('Conviction Score')) { const match = msg.text.match(/(\d+)\/100/); if (match) setConvictionScore(parseInt(match[1])); } setMessages(prev => [...prev, msg]); step++; } else { clearInterval(interval); setLoading(false); setIsDebating(false); } }, 1200); }; return (

AI Committee

{ticker && !isDebating && !convictionScore && ( )}
{!ticker && (
Waiting for selection...
)} {messages.map((msg, idx) => (
{agents[msg.agent].icon}
{agents[msg.agent].name} {msg.text}
))}
{loading && messages.length > 0 && messages.length < 4 && ( Typing... )}
{convictionScore !== null && (
Conviction Score
= 70 ? 'text-green-600' : convictionScore >= 40 ? 'text-gs-gold' : 'text-red-500'}`}> {convictionScore} / 100
)}
); }