From c6e5f83de8e70596eff8552c946bca31f093c42a Mon Sep 17 00:00:00 2001 From: abhi1992002 Date: Fri, 30 Jan 2026 15:02:33 +0530 Subject: [PATCH] feat(chat): update chat page layout and enhance message handling - Refactored the chat page to utilize a new `ChatSidebar` component for better organization and user experience. - Improved message handling by simplifying session creation logic and ensuring proper state management. - Updated UI elements for consistency, including button labels and input handling. - Enhanced message rendering to support tool call outputs, improving the chat interaction flow. These changes aim to streamline the chat interface and improve overall usability. --- autogpt_platform/frontend/pnpm-lock.yaml | 62 ++++++++ .../src/app/(platform)/copilot-2/page.tsx | 144 +++++++++--------- .../components/ChatSidebar/ChatSidebar.tsx | 45 ++++++ 3 files changed, 175 insertions(+), 76 deletions(-) create mode 100644 autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/components/ChatSidebar/ChatSidebar.tsx diff --git a/autogpt_platform/frontend/pnpm-lock.yaml b/autogpt_platform/frontend/pnpm-lock.yaml index a7ee54d188..ff5dca640f 100644 --- a/autogpt_platform/frontend/pnpm-lock.yaml +++ b/autogpt_platform/frontend/pnpm-lock.yaml @@ -191,6 +191,9 @@ importers: moment: specifier: 2.30.1 version: 2.30.1 + motion: + specifier: 12.29.2 + version: 12.29.2(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next: specifier: 15.4.10 version: 15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -5164,6 +5167,20 @@ packages: react-dom: optional: true + framer-motion@12.29.2: + resolution: {integrity: sha512-lSNRzBJk4wuIy0emYQ/nfZ7eWhqud2umPKw2QAQki6uKhZPKm2hRQHeQoHTG9MIvfobb+A/LbEWPJU794ZUKrg==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -6175,9 +6192,29 @@ packages: motion-dom@12.24.8: resolution: {integrity: sha512-wX64WITk6gKOhaTqhsFqmIkayLAAx45SVFiMnJIxIrH5uqyrwrxjrfo8WX9Kh8CaUAixjeMn82iH0W0QT9wD5w==} + motion-dom@12.29.2: + resolution: {integrity: sha512-/k+NuycVV8pykxyiTCoFzIVLA95Nb1BFIVvfSu9L50/6K6qNeAYtkxXILy/LRutt7AzaYDc2myj0wkCVVYAPPA==} + motion-utils@12.23.28: resolution: {integrity: sha512-0W6cWd5Okoyf8jmessVK3spOmbyE0yTdNKujHctHH9XdAE4QDuZ1/LjSXC68rrhsJU+TkzXURC5OdSWh9ibOwQ==} + motion-utils@12.29.2: + resolution: {integrity: sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==} + + motion@12.29.2: + resolution: {integrity: sha512-jMpHdAzEDF1QQ055cB+1lOBLdJ6ialVWl6QQzpJI2OvmHequ7zFVHM2mx0HNAy+Tu4omUlApfC+4vnkX0geEOg==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -13602,6 +13639,16 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + framer-motion@12.29.2(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + motion-dom: 12.29.2 + motion-utils: 12.29.2 + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 1.2.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 @@ -14896,8 +14943,23 @@ snapshots: dependencies: motion-utils: 12.23.28 + motion-dom@12.29.2: + dependencies: + motion-utils: 12.29.2 + motion-utils@12.23.28: {} + motion-utils@12.29.2: {} + + motion@12.29.2(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + framer-motion: 12.29.2(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 1.2.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + ms@2.1.3: {} msw-storybook-addon@2.0.6(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3)): diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/page.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot-2/page.tsx index e12f95d6fe..8a62a808eb 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot-2/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/page.tsx @@ -1,26 +1,28 @@ -'use client'; +"use client"; -import { useChat } from '@ai-sdk/react'; -import { DefaultChatTransport } from 'ai'; -import { useState, useMemo } from 'react'; -import { parseAsString, useQueryState } from 'nuqs'; -import { postV2CreateSession } from '@/app/api/__generated__/endpoints/chat/chat'; +import { useChat } from "@ai-sdk/react"; +import { DefaultChatTransport } from "ai"; +import { useState, useMemo } from "react"; +import { parseAsString, useQueryState } from "nuqs"; +import { ChatSidebar } from "./tools/components/ChatSidebar/ChatSidebar"; export default function Page() { - const [sessionId, setSessionId] = useQueryState('sessionId', parseAsString); + const [sessionId] = useQueryState("sessionId", parseAsString); const [isCreating, setIsCreating] = useState(false); - const [input, setInput] = useState(''); + const [input, setInput] = useState(""); const transport = useMemo(() => { if (!sessionId) return null; return new DefaultChatTransport({ api: `/api/chat/sessions/${sessionId}/stream`, - prepareSendMessagesRequest: ({ id, messages, trigger, messageId }) => { + prepareSendMessagesRequest: ({ messages }) => { const last = messages[messages.length - 1]; return { body: { - message: last.parts?.map((p) => (p.type === 'text' ? p.text : '')).join(''), - is_user_message: last.role === 'user', + message: last.parts + ?.map((p) => (p.type === "text" ? p.text : "")) + .join(""), + is_user_message: last.role === "user", context: null, }, }; @@ -32,59 +34,22 @@ export default function Page() { transport: transport ?? undefined, }); - async function createSession(): Promise { - setIsCreating(true); - try { - const response = await postV2CreateSession({ - body: JSON.stringify({}), - }); - if (response.status === 200 && response.data?.id) { - return response.data.id; - } - console.error('[Copilot2] Failed to create session:', response); - return null; - } catch (error) { - console.error('[Copilot2] Error creating session:', error); - return null; - } finally { - setIsCreating(false); - } - } - - async function handleNewSession() { - const newSessionId = await createSession(); - if (newSessionId) { - setSessionId(newSessionId); - } - } - async function handleStartChat(prompt: string) { - if (!prompt.trim() || isCreating) return; - - const newSessionId = await createSession(); - if (newSessionId) { - await setSessionId(newSessionId); - sendMessage({ text: prompt }); - setInput(''); - } + if (!prompt.trim()) return; + sendMessage({ text: prompt }); + setInput(""); } // Show landing page when no session exists if (!sessionId) { return (
-
- -
+
-

Start a new conversation

+

+ Start a new conversation +

{ e.preventDefault(); @@ -104,7 +69,7 @@ export default function Page() { disabled={isCreating || !input.trim()} className="mt-2 w-full rounded-md bg-blue-600 px-4 py-2 text-white transition-colors hover:bg-blue-700 disabled:opacity-50" > - {isCreating ? 'Starting...' : 'Start Chat'} + {isCreating ? "Starting..." : "Start Chat"}
@@ -115,28 +80,55 @@ export default function Page() { return (
{/* Sidebar */} -
- -
+ {/* Chat area */} -
-
Session ID: {sessionId}
+
+
+ Session ID: {sessionId} +
+
{messages.map((message) => ( -
- {message.role === 'user' ? 'User: ' : 'AI: '} - {message.parts.map((part, index) => - part.type === 'text' ?

{part.text}

: null, - )} +
+ {message.parts.map((part, index) => { + if (part.type === "tool-find_block") { + return ( +
+ {part.state === "input-streaming" && ( +

Finding blocks for you

+ )} + {part.state === "input-available" && ( +

+ Searching blocks for{" "} + {(part.input as { query: string }).query} +

+ )} + {part.state === "output-available" && ( +

+ Found + { + ( + JSON.parse(part.output as string) as { + count: number; + } + ).count + } + blocks +

+ )} +
+ ); + } else if (part.type === "text") { + return

{part.text}

; + } + })}
))} + {status === "submitted" &&

Thinking....

} {error &&
Error: {error.message}
}
@@ -145,21 +137,21 @@ export default function Page() { e.preventDefault(); if (input.trim()) { sendMessage({ text: input }); - setInput(''); + setInput(""); } }} > setInput(e.target.value)} - disabled={status !== 'ready'} + disabled={status !== "ready"} placeholder="Say something..." /> -
); -} \ No newline at end of file +} diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/components/ChatSidebar/ChatSidebar.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/components/ChatSidebar/ChatSidebar.tsx new file mode 100644 index 0000000000..fca1fd897a --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/copilot-2/tools/components/ChatSidebar/ChatSidebar.tsx @@ -0,0 +1,45 @@ +import { useState } from "react"; +import { postV2CreateSession } from "@/app/api/__generated__/endpoints/chat/chat"; +import { parseAsString, useQueryState } from "nuqs"; + +export const ChatSidebar = ({ isCreating, setIsCreating }: { isCreating: boolean, setIsCreating: (isCreating: boolean) => void }) => { + const [sessionId, setSessionId] = useQueryState("sessionId", parseAsString); + + async function createSession(): Promise { + setIsCreating(true); + try { + const response = await postV2CreateSession({ + body: JSON.stringify({}), + }); + if (response.status === 200 && response.data?.id) { + return response.data.id; + } + return null; + } catch (error) { + return null; + } finally { + setIsCreating(false); + } + } + + async function handleNewSession() { + const newSessionId = await createSession(); + if (newSessionId) { + setSessionId(newSessionId); + } + } + + + return ( +
+ +
+ + ); +}; \ No newline at end of file