diff options
Diffstat (limited to 'frontend/src/components/ChatInterface.jsx')
| -rw-r--r-- | frontend/src/components/ChatInterface.jsx | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/frontend/src/components/ChatInterface.jsx b/frontend/src/components/ChatInterface.jsx new file mode 100644 index 0000000..951183f --- /dev/null +++ b/frontend/src/components/ChatInterface.jsx @@ -0,0 +1,117 @@ +import { useState, useEffect, useRef } from 'react'; +import ReactMarkdown from 'react-markdown'; +import Stage1 from './Stage1'; +import Stage2 from './Stage2'; +import Stage3 from './Stage3'; +import './ChatInterface.css'; + +export default function ChatInterface({ + conversation, + onSendMessage, + isLoading, +}) { + const [input, setInput] = useState(''); + const messagesEndRef = useRef(null); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }; + + useEffect(() => { + scrollToBottom(); + }, [conversation]); + + const handleSubmit = (e) => { + e.preventDefault(); + if (input.trim() && !isLoading) { + onSendMessage(input); + setInput(''); + } + }; + + const handleKeyDown = (e) => { + // Submit on Enter (without Shift) + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSubmit(e); + } + }; + + if (!conversation) { + return ( + <div className="chat-interface"> + <div className="empty-state"> + <h2>Welcome to LLM Council</h2> + <p>Create a new conversation to get started</p> + </div> + </div> + ); + } + + return ( + <div className="chat-interface"> + <div className="messages-container"> + {conversation.messages.length === 0 ? ( + <div className="empty-state"> + <h2>Start a conversation</h2> + <p>Ask a question to consult the LLM Council</p> + </div> + ) : ( + conversation.messages.map((msg, index) => ( + <div key={index} className="message-group"> + {msg.role === 'user' ? ( + <div className="user-message"> + <div className="message-label">You</div> + <div className="message-content"> + <div className="markdown-content"> + <ReactMarkdown>{msg.content}</ReactMarkdown> + </div> + </div> + </div> + ) : ( + <div className="assistant-message"> + <div className="message-label">LLM Council</div> + <Stage1 responses={msg.stage1} /> + <Stage2 + rankings={msg.stage2} + labelToModel={msg.metadata?.label_to_model} + aggregateRankings={msg.metadata?.aggregate_rankings} + /> + <Stage3 finalResponse={msg.stage3} /> + </div> + )} + </div> + )) + )} + + {isLoading && ( + <div className="loading-indicator"> + <div className="spinner"></div> + <span>Consulting the council...</span> + </div> + )} + + <div ref={messagesEndRef} /> + </div> + + <form className="input-form" onSubmit={handleSubmit}> + <textarea + className="message-input" + placeholder="Ask your question... (Shift+Enter for new line, Enter to send)" + value={input} + onChange={(e) => setInput(e.target.value)} + onKeyDown={handleKeyDown} + disabled={isLoading} + rows={3} + /> + <button + type="submit" + className="send-button" + disabled={!input.trim() || isLoading} + > + Send + </button> + </form> + </div> + ); +} |
