diff --git a/apps/rowboat/app/projects/[projectId]/playground/app.tsx b/apps/rowboat/app/projects/[projectId]/playground/app.tsx index 864036fc..858994f6 100644 --- a/apps/rowboat/app/projects/[projectId]/playground/app.tsx +++ b/apps/rowboat/app/projects/[projectId]/playground/app.tsx @@ -1,5 +1,5 @@ 'use client'; -import { useState } from "react"; +import { useState, useCallback, useRef } from "react"; import { z } from "zod"; import { MCPServer, PlaygroundChat } from "@/app/lib/types/types"; import { Workflow } from "@/app/lib/types/workflow_types"; @@ -30,7 +30,6 @@ export function App({ mcpServerUrls: Array>; toolWebhookUrl: string; }) { - const [counter, setCounter] = useState(0); const [testProfile, setTestProfile] = useState> | null>(null); const [systemMessage, setSystemMessage] = useState(defaultSystemMessage); const [chat, setChat] = useState>({ @@ -42,19 +41,17 @@ export function App({ }); const [isProfileSelectorOpen, setIsProfileSelectorOpen] = useState(false); const [showCopySuccess, setShowCopySuccess] = useState(false); + const getCopyContentRef = useRef<(() => string) | null>(null); function handleSystemMessageChange(message: string) { setSystemMessage(message); - setCounter(counter + 1); } function handleTestProfileChange(profile: WithStringId> | null) { setTestProfile(profile); - setCounter(counter + 1); } function handleNewChatButtonClick() { - setCounter(counter + 1); setChat({ projectId, createdAt: new Date().toISOString(), @@ -62,21 +59,23 @@ export function App({ simulated: false, systemMessage: defaultSystemMessage, }); + setSystemMessage(defaultSystemMessage); } - const handleCopyJson = () => { - const jsonString = JSON.stringify({ - messages: [{ - role: 'system', - content: systemMessage, - }, ...chat.messages], - }, null, 2); - navigator.clipboard.writeText(jsonString); - setShowCopySuccess(true); - setTimeout(() => { - setShowCopySuccess(false); - }, 2000); - }; + const handleCopyJson = useCallback(() => { + if (getCopyContentRef.current) { + try { + const data = getCopyContentRef.current(); + navigator.clipboard.writeText(data); + setShowCopySuccess(true); + setTimeout(() => { + setShowCopySuccess(false); + }, 2000); + } catch (error) { + console.error('Error copying:', error); + } + } + }, []); if (hidden) { return <>; @@ -140,7 +139,6 @@ export function App({ />
{ getCopyContentRef.current = fn; }} />
diff --git a/apps/rowboat/app/projects/[projectId]/playground/components/chat.tsx b/apps/rowboat/app/projects/[projectId]/playground/components/chat.tsx index af50c465..f22ed662 100644 --- a/apps/rowboat/app/projects/[projectId]/playground/components/chat.tsx +++ b/apps/rowboat/app/projects/[projectId]/playground/components/chat.tsx @@ -1,5 +1,5 @@ 'use client'; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState, useCallback } from "react"; import { getAssistantResponseStreamId } from "@/app/actions/actions"; import { Messages } from "./messages"; import z from "zod"; @@ -27,6 +27,7 @@ export function Chat({ onSystemMessageChange, mcpServerUrls, toolWebhookUrl, + onCopyClick, }: { chat: z.infer; projectId: string; @@ -38,6 +39,7 @@ export function Chat({ onSystemMessageChange: (message: string) => void; mcpServerUrls: Array>; toolWebhookUrl: string; + onCopyClick: (fn: () => string) => void; }) { const [messages, setMessages] = useState[]>(chat.messages); const [loadingAssistantResponse, setLoadingAssistantResponse] = useState(false); @@ -47,9 +49,23 @@ export function Chat({ const [fetchResponseError, setFetchResponseError] = useState(null); const [lastAgenticRequest, setLastAgenticRequest] = useState(null); const [lastAgenticResponse, setLastAgenticResponse] = useState(null); - const [isProfileSelectorOpen, setIsProfileSelectorOpen] = useState(false); const [optimisticMessages, setOptimisticMessages] = useState[]>(chat.messages); - const messagesEndRef = useRef(null); + + const getCopyContent = useCallback(() => { + return JSON.stringify({ + messages: [{ + role: 'system', + content: systemMessage, + }, ...messages], + lastRequest: lastAgenticRequest, + lastResponse: lastAgenticResponse, + }, null, 2); + }, [messages, systemMessage, lastAgenticRequest, lastAgenticResponse]); + + // Expose copy function to parent + useEffect(() => { + onCopyClick(getCopyContent); + }, [getCopyContent, onCopyClick]); // reset optimistic messages when messages change useEffect(() => { @@ -63,7 +79,6 @@ export function Chat({ .forEach((message) => { toolCallResults[message.tool_call_id] = message; }); - console.log('toolCallResults', toolCallResults); function handleUserMessage(prompt: string) { const updatedMessages: z.infer[] = [...messages, { @@ -94,7 +109,6 @@ export function Chat({ // get assistant response useEffect(() => { - console.log('stream useEffect called'); let ignore = false; let eventSource: EventSource | null = null; let msgs: z.infer[] = []; @@ -102,6 +116,11 @@ export function Chat({ async function process() { setLoadingAssistantResponse(true); setFetchResponseError(null); + + // Reset request/response state before making new request + setLastAgenticRequest(null); + setLastAgenticResponse(null); + const { agents, tools, prompts, startAgent } = convertWorkflowToAgenticAPI(workflow); const request: z.infer = { projectId, @@ -121,8 +140,9 @@ export function Chat({ toolWebhookUrl: toolWebhookUrl, testProfile: testProfile ?? undefined, }; - setLastAgenticRequest(null); - setLastAgenticResponse(null); + + // Store the full request object + setLastAgenticRequest(request); let streamId: string | null = null; try { @@ -139,14 +159,9 @@ export function Chat({ } if (ignore || !streamId) { - console.log('almost there', ignore, streamId); return; } - // log the stream id - console.log('🔄 got assistant response', streamId); - - // read from SSE stream eventSource = new EventSource(`/api/v1/stream-response/${streamId}`); eventSource.addEventListener("message", (event) => { @@ -158,7 +173,6 @@ export function Chat({ const data = JSON.parse(event.data); const msg = AgenticAPIChatMessage.parse(data); const parsedMsg = convertFromAgenticAPIChatMessages([msg])[0]; - console.log('🔄 got assistant response chunk', parsedMsg); msgs.push(parsedMsg); setOptimisticMessages(prev => [...prev, parsedMsg]); } catch (err) { @@ -173,9 +187,15 @@ export function Chat({ eventSource.close(); } - console.log('🔄 got assistant response done', event.data); - const parsed: {state: unknown} = JSON.parse(event.data); + const parsed = JSON.parse(event.data); setAgenticState(parsed.state); + + // Combine state and collected messages in the response + setLastAgenticResponse({ + ...parsed, + messages: msgs + }); + setMessages([...messages, ...msgs]); setLoadingAssistantResponse(false); }); @@ -207,7 +227,6 @@ export function Chat({ return () => { ignore = true; - console.log('stream useEffect cleanup called'); if (eventSource) { eventSource.close(); }