From 1e74bb2bf3698504d8aa57221cea7be6c57871cf Mon Sep 17 00:00:00 2001 From: Dylan Huang Date: Sat, 20 Sep 2025 15:08:17 -0700 Subject: [PATCH 1/5] TODO: implement agent --- vite-app/src/App.tsx | 39 +++++++++----- vite-app/src/agent/agent.ts | 14 +++++ vite-app/src/components/ChatMessages.tsx | 48 +++++++++++++++++ vite-app/src/components/ChatWindow.tsx | 68 ++++++++++++++++++++++++ vite-app/src/components/Textarea.tsx | 25 +++++++++ 5 files changed, 181 insertions(+), 13 deletions(-) create mode 100644 vite-app/src/agent/agent.ts create mode 100644 vite-app/src/components/ChatMessages.tsx create mode 100644 vite-app/src/components/ChatWindow.tsx create mode 100644 vite-app/src/components/Textarea.tsx diff --git a/vite-app/src/App.tsx b/vite-app/src/App.tsx index f322333d..baacc7b2 100644 --- a/vite-app/src/App.tsx +++ b/vite-app/src/App.tsx @@ -4,6 +4,7 @@ import { observer } from "mobx-react"; import Dashboard from "./components/Dashboard"; import Button from "./components/Button"; import StatusIndicator from "./components/StatusIndicator"; +import { ChatWindow } from "./components/ChatWindow"; import { EvaluationRowSchema, type EvaluationRow } from "./types/eval-protocol"; import { WebSocketServerMessageSchema } from "./types/websocket"; import { GlobalState } from "./GlobalState"; @@ -137,7 +138,7 @@ const App = observer(() => { return (
-
- - } /> - } - /> - } - /> - +
+
+ {/* Left side - Main content (2/3 width) */} +
+ + } /> + } + /> + } + /> + +
+ + {/* Right side - Chat window (1/3 width) - Sticky */} +
+
+ +
+
+
); diff --git a/vite-app/src/agent/agent.ts b/vite-app/src/agent/agent.ts new file mode 100644 index 00000000..62a58e7b --- /dev/null +++ b/vite-app/src/agent/agent.ts @@ -0,0 +1,14 @@ +import OpenAI from "openai"; + +export class Agent { + client: OpenAI; + /** + * This is an agent that has access to tools that will allow it to search + * through eval results + */ + constructor() { + this.client = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, + }); + } +} diff --git a/vite-app/src/components/ChatMessages.tsx b/vite-app/src/components/ChatMessages.tsx new file mode 100644 index 00000000..fda4eda7 --- /dev/null +++ b/vite-app/src/components/ChatMessages.tsx @@ -0,0 +1,48 @@ +import { useRef, useEffect } from "react"; +import type { Message } from "../types/eval-protocol"; +import { MessageBubble } from "./MessageBubble"; + +interface ChatMessagesProps { + messages: Message[]; +} + +export const ChatMessages = ({ messages }: ChatMessagesProps) => { + const scrollContainerRef = useRef(null); + const prevMessagesLengthRef = useRef(0); + + // Auto-scroll to bottom when new messages come in + useEffect(() => { + // On first render, just set the initial length without scrolling + if (prevMessagesLengthRef.current === 0) { + prevMessagesLengthRef.current = messages.length; + return; + } + + // Only scroll if we have messages and the number of messages has increased + // This prevents scrolling on initial mount or when messages are removed + if ( + messages.length > 0 && + messages.length > prevMessagesLengthRef.current + ) { + if (scrollContainerRef.current) { + scrollContainerRef.current.scrollTo({ + top: scrollContainerRef.current.scrollHeight, + behavior: "smooth", + }); + } + } + // Update the previous length for the next comparison + prevMessagesLengthRef.current = messages.length; + }, [messages]); + + return ( +
+ {messages.map((message, msgIndex) => ( + + ))} +
+ ); +}; diff --git a/vite-app/src/components/ChatWindow.tsx b/vite-app/src/components/ChatWindow.tsx new file mode 100644 index 00000000..51df0d6b --- /dev/null +++ b/vite-app/src/components/ChatWindow.tsx @@ -0,0 +1,68 @@ +import { useState } from "react"; +import { ChatMessages } from "./ChatMessages"; +import Textarea from "./Textarea"; +import type { Message } from "../types/eval-protocol"; + +interface ChatWindowProps { + className?: string; +} + +export const ChatWindow = ({ className = "" }: ChatWindowProps) => { + const [chatMessages, setChatMessages] = useState([]); + const [chatInput, setChatInput] = useState(""); + + const handleSendMessage = () => { + if (!chatInput.trim()) return; + + const userMessage: Message = { + role: "user", + content: chatInput.trim(), + }; + + setChatMessages((prev) => [...prev, userMessage]); + setChatInput(""); + + // Simulate AI response (you can replace this with actual AI integration) + setTimeout(() => { + const aiMessage: Message = { + role: "assistant", + content: `I received your message: "${userMessage.content}". This is a placeholder response. You can integrate with your AI service here.`, + }; + setChatMessages((prev) => [...prev, aiMessage]); + }, 1000); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + handleSendMessage(); + } + }; + + return ( +
+
+ {/* Chat header - following Dashboard pattern */} +
+

AI Assistant

+
+ + {/* Chat messages */} + + + {/* Chat input - following Dashboard pattern */} +
+