import { useState, useEffect, useRef, memo } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import Stage1 from './Stage1'; import Stage2 from './Stage2'; import Stage3 from './Stage3'; import './ChatInterface.css'; const remarkPlugins = [remarkGfm]; // Only memoize user messages (they never change once sent) const UserMessage = memo(function UserMessage({ content }) { return (
You
{content}
); }); // Memoize completed assistant messages, but skip memo for the active (last) one const AssistantMessage = memo(function AssistantMessage({ msg, isActive }) { return (
LLM Council
{/* Stage 1 */} {msg.loading?.stage1 && (
Running Stage 1: Collecting individual responses...
)} {msg.stage1 && } {/* Stage 2 */} {msg.loading?.stage2 && (
Running Stage 2: Peer rankings...
)} {msg.stage2 && ( )} {/* Stage 3 */} {msg.loading?.stage3 && (
Running Stage 3: Final synthesis...
)} {msg.stage3 && }
); }, (prevProps, nextProps) => { // If active (streaming), always re-render if (prevProps.isActive || nextProps.isActive) return false; // Otherwise skip re-render (completed messages don't change) return true; }); export default function ChatInterface({ conversation, onSendMessage, onStopGeneration, isLoading, pendingInput, onPendingInputConsumed, }) { const [input, setInput] = useState(''); const textareaRef = useRef(null); const messagesEndRef = useRef(null); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [conversation, isLoading]); // Recover input from stopped generation useEffect(() => { if (pendingInput !== null) { setInput(pendingInput); onPendingInputConsumed(); setTimeout(() => textareaRef.current?.focus(), 0); } }, [pendingInput]); const handleSubmit = (e) => { e.preventDefault(); if (input.trim() && !isLoading) { onSendMessage(input); setInput(''); } }; const handleKeyDown = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSubmit(e); } }; if (!conversation) { return (

Welcome to LLM Council

Create a new conversation to get started

); } return (
{conversation.messages.length === 0 ? (

Start a conversation

Ask a question to consult the LLM Council

) : ( conversation.messages.map((msg, index) => { if (msg.role === 'user') { return ; } const isLastAssistant = isLoading && index === conversation.messages.length - 1; return ; }) )} {isLoading && (
Consulting the council...
)}