From 606daa9f05f83cd730967f430c09c1e9a31e25b9 Mon Sep 17 00:00:00 2001 From: marko-kraemer Date: Tue, 15 Apr 2025 19:54:26 +0100 Subject: [PATCH] wip --- .../app/dashboard/agents/[threadId]/page.tsx | 344 ++------------- frontend/src/app/dashboard/page_og.tsx | 2 +- .../{chat => thread}/chat-input.tsx | 242 +++++----- .../src/components/thread/message-display.tsx | 416 ++++++++++++++++++ .../src/components/thread/message-list.tsx | 90 ++++ .../{chat => thread}/tool-components.tsx | 0 frontend/src/hooks/use-tools-panel.tsx | 2 +- 7 files changed, 688 insertions(+), 408 deletions(-) rename frontend/src/components/{chat => thread}/chat-input.tsx (55%) create mode 100644 frontend/src/components/thread/message-display.tsx create mode 100644 frontend/src/components/thread/message-list.tsx rename frontend/src/components/{chat => thread}/tool-components.tsx (100%) diff --git a/frontend/src/app/dashboard/agents/[threadId]/page.tsx b/frontend/src/app/dashboard/agents/[threadId]/page.tsx index 15a6f40d..a25604cf 100644 --- a/frontend/src/app/dashboard/agents/[threadId]/page.tsx +++ b/frontend/src/app/dashboard/agents/[threadId]/page.tsx @@ -3,16 +3,16 @@ import React, { useState, useEffect, useCallback, useRef, useContext } from "react"; import { getProject, getMessages, getThread, addUserMessage, startAgent, stopAgent, getAgentRuns, streamAgent, type Message, type Project, type Thread, type AgentRun } from "@/lib/api"; import { useRouter, useSearchParams } from "next/navigation"; -import { AlertCircle, Square, Send, User, Plus } from "lucide-react"; +import { AlertCircle } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; -import { Textarea } from "@/components/ui/textarea"; import { Skeleton } from "@/components/ui/skeleton"; -import { SUPPORTED_XML_TAGS, ParsedTag } from "@/lib/types/tool-calls"; +import { SUPPORTED_XML_TAGS } from "@/lib/types/tool-calls"; import { ToolCallsContext } from "@/app/providers"; -import { getComponentForTag } from "@/components/chat/tool-components"; import { BillingErrorAlert } from "@/components/billing/BillingErrorAlert"; import { useBillingError } from "@/hooks/useBillingError"; +import { MessageList } from "@/components/thread/message-list"; +import { ChatInput } from "@/components/thread/chat-input"; interface AgentPageProps { params: { @@ -21,9 +21,9 @@ interface AgentPageProps { } // Parse XML tags in content -function parseXMLTags(content: string): { parts: (string | ParsedTag)[], openTags: Record } { - const parts: (string | ParsedTag)[] = []; - const openTags: Record = {}; +function parseXMLTags(content: string): { parts: any[], openTags: Record } { + const parts: any[] = []; + const openTags: Record = {}; const tagStack: Array<{tagName: string, position: number}> = []; // Find all opening and closing tags @@ -85,7 +85,7 @@ function parseXMLTags(content: string): { parts: (string | ParsedTag)[], openTag } // Create tag object with unique ID - const parsedTag: ParsedTag = { + const parsedTag: any = { tagName, attributes, content: '', @@ -112,7 +112,7 @@ function parseXMLTags(content: string): { parts: (string | ParsedTag)[], openTag for (let i = tagStack.length - 1; i >= 0; i--) { if (tagStack[i].tagName === tagName) { const openTagIndex = tagStack[i].position; - const openTag = parts[openTagIndex] as ParsedTag; + const openTag = parts[openTagIndex] as any; // Get content between this opening and closing tag pair const contentStart = position; @@ -193,41 +193,12 @@ function parseXMLTags(content: string): { parts: (string | ParsedTag)[], openTag return { parts, openTags }; } -// Simple component to handle message formatting with XML tag support -function MessageContent({ content }: { content: string }) { - const { parts, openTags } = parseXMLTags(content); - - return ( -
- {parts.map((part, index) => { - if (typeof part === 'string') { - return ( - - {part.split('\n').map((line, i) => ( - - {line} - {i < part.split('\n').length - 1 &&
} -
- ))} -
- ); - } else { - // Render specialized tool component based on tag type - const ToolComponent = getComponentForTag(part); - return ; - } - })} -
- ); -} - export default function AgentPage({ params }: AgentPageProps) { const resolvedParams = React.use(params as any) as { threadId: string }; const { threadId } = resolvedParams; const router = useRouter(); const searchParams = useSearchParams(); const initialMessage = searchParams.get('message'); - const messagesEndRef = useRef(null); const streamCleanupRef = useRef<(() => void) | null>(null); const [agent, setAgent] = useState(null); @@ -254,7 +225,7 @@ export default function AgentPage({ params }: AgentPageProps) { console.log(`[TOOLS] Processing ${allContent.length} content items for tool calls`); // Create a new array of tags with a better deduplication strategy - const extractedTags: ParsedTag[] = []; + const extractedTags: any[] = []; const seenTagIds = new Set(); allContent.forEach((content, idx) => { @@ -342,8 +313,8 @@ export default function AgentPage({ params }: AgentPageProps) { }); // Try to pair tool calls with their results - const pairedTags: ParsedTag[] = []; - const callsByTagName: Record = {}; + const pairedTags: any[] = []; + const callsByTagName: Record = {}; // Group by tag name first extractedTags.forEach(tag => { @@ -396,11 +367,6 @@ export default function AgentPage({ params }: AgentPageProps) { setToolCalls(pairedTags); }, [messages, streamContent, setToolCalls, agent]); - // Scroll to bottom of messages - const scrollToBottom = useCallback(() => { - messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); - }, []); - // Load initial data useEffect(() => { async function loadData() { @@ -470,11 +436,6 @@ export default function AgentPage({ params }: AgentPageProps) { }; }, [threadId, initialMessage, router]); - // Scroll to bottom when messages change - useEffect(() => { - scrollToBottom(); - }, [messages, streamContent, scrollToBottom]); - // Handle streaming agent responses const handleStreamAgent = useCallback((agentRunId: string) => { // Clean up any existing stream first @@ -645,8 +606,8 @@ export default function AgentPage({ params }: AgentPageProps) { }, [threadId, conversation, handleBillingError]); // Handle sending a message - const handleSendMessage = async () => { - if (!userMessage.trim() || isSending) return; + const handleSendMessage = async (message: string) => { + if (!message.trim() || isSending) return; if (!conversation) return; setIsSending(true); @@ -658,7 +619,7 @@ export default function AgentPage({ params }: AgentPageProps) { const userMsg: Message = { type: 'user', role: 'user', - content: userMessage, + content: message, }; setMessages(prev => [...prev, userMsg]); @@ -666,7 +627,7 @@ export default function AgentPage({ params }: AgentPageProps) { setUserMessage(""); // Add user message to API and start agent - await addUserMessage(conversation.thread_id, userMessage); + await addUserMessage(conversation.thread_id, message); const agentResponse = await startAgent(conversation.thread_id); // Set current agent run ID and start streaming @@ -751,130 +712,25 @@ export default function AgentPage({ params }: AgentPageProps) { isOpen={true} />
-
- {messages.length === 0 && !streamContent ? ( -
-
-

Start a conversation

-

- Send a message to start talking with {agent?.name || "the AI agent"} -

-
-
- ) : ( - <> - {messages.map((message, index) => { - // Skip messages containing "ToolResult(" - if (!message || !message?.content || !message?.role) { - return null; - } - - if (message.content.includes("ToolResult(")) { - return null; - } - - return ( -
-
- {message.role === "user" && ( - - )} - -
-
- ); - })} - - {/* Show streaming content if available */} - {streamContent && ( -
-
-
- - {isStreaming && ( - - )} - -
-
-
- )} - - {/* Show a loading indicator if the agent is running but no stream yet */} - {isAgentRunning && !streamContent && ( -
-
-
-
-
-
- AI is thinking... -
-
-
- )} - - )} -
-
+ -
-
-