diff --git a/src/interface/web/app/agents/page.tsx b/src/interface/web/app/agents/page.tsx index a8fd1112..f5642893 100644 --- a/src/interface/web/app/agents/page.tsx +++ b/src/interface/web/app/agents/page.tsx @@ -71,8 +71,9 @@ async function openChat(slug: string, userData: UserProfile | null) { } const response = await fetch(`/api/chat/sessions?agent_slug=${slug}`, { method: "POST" }); + const data = await response.json(); if (response.status == 200) { - window.location.href = `/chat`; + window.location.href = `/chat?conversationId=${data.conversation_id}`; } else if (response.status == 403 || response.status == 401) { window.location.href = unauthenticatedRedirectUrl; } else { @@ -294,7 +295,7 @@ export default function Agents() { return (
- Talk to a Specialized Agent + Agents
Error loading agents @@ -307,7 +308,7 @@ export default function Agents() { return (
- Talk to a Specialized Agent + Agents
booting up your agents diff --git a/src/interface/web/app/chat/chat.module.css b/src/interface/web/app/chat/chat.module.css index b9bd68c7..ba9d968e 100644 --- a/src/interface/web/app/chat/chat.module.css +++ b/src/interface/web/app/chat/chat.module.css @@ -4,10 +4,14 @@ div.main { } .suggestions { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + display: flex; + overflow-x: none; + height: 50%; + padding: 10px; + white-space: nowrap; + /* grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); */ gap: 1rem; - justify-content: center; + /* justify-content: center; */ } div.inputBox { @@ -124,3 +128,33 @@ div.agentIndicator { } } + +#pink { + background-color: #f8d1f8; + color: #000000; + background-image: linear-gradient(to top, #f8d1f8 1%, white 100%); +} + +#blue { + background-color: #d1f8f8; + color: #000000; + background-image: linear-gradient(to top, #d1f8f8 1%, white 100%); +} + +#green { + background-color: #d1f8d1; + color: #000000; + background-image: linear-gradient(to top, #d1f8d1 1%, white 100%); +} + +#purple { + background-color: #f8d1f8; + color: #000000; + background-image: linear-gradient(to top, #f8d1f8 1%, white 100%); +} + +#yellow { + background-color: #f8f8d1; + color: #000000; + background-image: linear-gradient(to top, #f8f8d1 1%, white 100%); +} diff --git a/src/interface/web/app/chat/page.tsx b/src/interface/web/app/chat/page.tsx index 325cf4f0..db770b89 100644 --- a/src/interface/web/app/chat/page.tsx +++ b/src/interface/web/app/chat/page.tsx @@ -3,7 +3,6 @@ import styles from './chat.module.css'; import React, { Suspense, useEffect, useState } from 'react'; -import SuggestionCard from '../components/suggestions/suggestionCard'; import SidePanel from '../components/sidePanel/chatHistorySidePanel'; import ChatHistory from '../components/chatHistory/chatHistory'; import NavMenu from '../components/navMenu/navMenu'; @@ -19,9 +18,6 @@ import { welcomeConsole } from '../common/utils'; import ChatInputArea, { ChatOptions } from '../components/chatInputArea/chatInputArea'; import { useAuthenticatedData } from '../common/auth'; - -const styleClassOptions = ['pink', 'blue', 'green', 'yellow', 'purple']; - interface ChatBodyDataProps { chatOptionsData: ChatOptions | null; setTitle: (title: string) => void; @@ -40,43 +36,38 @@ function ChatBodyData(props: ChatBodyDataProps) { const [processingMessage, setProcessingMessage] = useState(false); useEffect(() => { - if (conversationId) { - props.onConversationIdChange?.(conversationId); + const storedMessage = localStorage.getItem("message"); + if (storedMessage) { + setMessage(storedMessage); } - }, [conversationId, props.onConversationIdChange]); + }, []); useEffect(() => { - if (message) { + if(message){ setProcessingMessage(true); props.setQueryToProcess(message); } }, [message]); + useEffect(() => { + if (conversationId) { + props.onConversationIdChange?.(conversationId); + } + }, [conversationId]); + useEffect(() => { if (props.streamedMessages && props.streamedMessages.length > 0 && props.streamedMessages[props.streamedMessages.length - 1].completed) { - setProcessingMessage(false); } else { setMessage(''); } }, [props.streamedMessages]); - if (!conversationId) { - return ( -
- {props.chatOptionsData && Object.entries(props.chatOptionsData).map(([key, value]) => ( - - ))} -
- ); + if(!conversationId) { + window.location.href = '/'; + return; } return ( @@ -88,7 +79,7 @@ function ChatBodyData(props: ChatBodyDataProps) { pendingMessage={processingMessage ? message : ''} incomingMessages={props.streamedMessages} />
-
+
setMessage(message)} @@ -121,7 +112,6 @@ export default function Chat() { const handleWebSocketMessage = (event: MessageEvent) => { let chunk = event.data; let currentMessage = messages.find(message => !message.completed); - if (!currentMessage) { console.error("No current message found"); return; @@ -205,51 +195,72 @@ export default function Chat() { }, []); useEffect(() => { - if (chatWS && queryToProcess) { - // Add a new object to the state - const newStreamMessage: StreamMessage = { - rawResponse: "", - trainOfThought: [], - context: [], - onlineContext: {}, - completed: false, - timestamp: (new Date()).toISOString(), - rawQuery: queryToProcess || "", - } - setMessages(prevMessages => [...prevMessages, newStreamMessage]); - setProcessQuerySignal(true); - } else { - if (!chatWS) { - console.error("No WebSocket connection available"); - } - if (!queryToProcess) { - console.error("No query to process"); - } - } - }, [queryToProcess]); + if (chatWS) { + chatWS.onmessage = handleWebSocketMessage; + } + }, [chatWS, messages]); + + //same as ChatBodyData for local storage message + useEffect(() => { + const storedMessage = localStorage.getItem("message"); + setQueryToProcess(storedMessage || ''); + }, []); useEffect(() => { - if (processQuerySignal && chatWS) { + if (chatWS && queryToProcess) { + const newStreamMessage: StreamMessage = { + rawResponse: "", + trainOfThought: [], + context: [], + onlineContext: {}, + completed: false, + timestamp: (new Date()).toISOString(), + rawQuery: queryToProcess || "", + }; + setMessages(prevMessages => [...prevMessages, newStreamMessage]); + + if (chatWS.readyState === WebSocket.OPEN) { + chatWS.send(queryToProcess); + setProcessQuerySignal(true); + } + else { + console.error("WebSocket is not open. ReadyState:", chatWS.readyState); + } + + setQueryToProcess(''); + } + }, [queryToProcess, chatWS]); + + useEffect(() => { + if (processQuerySignal && chatWS && chatWS.readyState === WebSocket.OPEN) { setProcessQuerySignal(false); chatWS.onmessage = handleWebSocketMessage; - chatWS?.send(queryToProcess); + chatWS.send(queryToProcess); + localStorage.removeItem("message"); } - }, [processQuerySignal]); + }, [processQuerySignal, chatWS]); useEffect(() => { - (async () => { - if (conversationId) { + const setupWebSocketConnection = async () => { + if (conversationId && (!chatWS || chatWS.readyState === WebSocket.CLOSED)) { + if(queryToProcess) { + const newWS = await setupWebSocket(conversationId, queryToProcess); + localStorage.removeItem("message"); + setChatWS(newWS); + } + else { const newWS = await setupWebSocket(conversationId); setChatWS(newWS); } - })(); + } + }; + setupWebSocketConnection(); }, [conversationId]); const handleConversationIdChange = (newConversationId: string) => { setConversationID(newConversationId); }; - if (isLoading) { return ; } @@ -260,7 +271,7 @@ export default function Chat() { {title} -
+
= { + 'red': 'border-red-500', + 'blue': 'border-blue-500', + 'green': 'border-green-500', + 'yellow': 'border-yellow-500', + 'purple': 'border-purple-500', + 'pink': 'border-pink-500', + 'indigo': 'border-indigo-500', + 'gray': 'border-gray-500', + 'orange': 'border-orange-500', +}; diff --git a/src/interface/web/app/common/iconUtils.tsx b/src/interface/web/app/common/iconUtils.tsx new file mode 100644 index 00000000..89fbfb27 --- /dev/null +++ b/src/interface/web/app/common/iconUtils.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { convertColorToTextClass } from './colorUtils'; +import { + Lightbulb, + Robot, + Aperture, + GraduationCap, + Jeep, + Island, + MathOperations, + Asclepius, + Couch, + Code, + Atom, + ClockCounterClockwise, + PaperPlaneTilt, + Info, + UserCircle, + Globe, + Palette, + LinkBreak, +} from "@phosphor-icons/react"; + +interface IconMap { + [key: string]: (color: string, width: string, height: string) => JSX.Element | null; +} + +const iconMap: IconMap = { + Lightbulb: (color: string, width: string, height: string) => , + Robot: (color: string, width: string, height: string) => , + Aperture: (color: string, width: string, height: string) => , + GraduationCap: (color: string, width: string, height: string) => , + Jeep: (color: string, width: string, height: string) => , + Island: (color: string, width: string, height: string) => , + MathOperations: (color: string, width: string, height: string) => , + Asclepius: (color: string, width: string, height: string) => , + Couch: (color: string, width: string, height: string) => , + Code: (color: string, width: string, height: string) => , + Atom: (color: string, width: string, height: string) => , + ClockCounterClockwise: (color: string, width: string, height: string) => , + Globe: (color: string, width: string, height: string) => , + Palette: (color: string, width: string, height: string) => , +}; + +function getIconFromIconName(iconName: string, color: string = 'gray', width: string = 'w-6', height: string = 'h-6') { + const icon = iconMap[iconName]; + const colorName = color.toLowerCase(); + const colorClass = convertColorToTextClass(colorName); + + return icon ? icon(colorClass, width, height) : null; +} + +export { getIconFromIconName }; diff --git a/src/interface/web/app/components/chatHistory/chatHistory.tsx b/src/interface/web/app/components/chatHistory/chatHistory.tsx index f4bd1c98..3fa2d91f 100644 --- a/src/interface/web/app/components/chatHistory/chatHistory.tsx +++ b/src/interface/web/app/components/chatHistory/chatHistory.tsx @@ -177,7 +177,7 @@ export default function ChatHistory(props: ChatHistoryProps) { .then(response => response.json()) .then((chatData: ChatResponse) => { props.setTitle(chatData.response.slug); - if (chatData && chatData.response && chatData.response.chat.length > 0) { + if (chatData && chatData.response && chatData.response.chat && chatData.response.chat.length > 0) { if (chatData.response.chat.length === data?.chat.length) { setHasMoreMessages(false); @@ -192,7 +192,18 @@ export default function ChatHistory(props: ChatHistoryProps) { } setFetchingData(false); } else { + if (chatData.response.agent && chatData.response.conversation_id) { + const chatMetadata ={ + chat: [], + agent: chatData.response.agent, + conversation_id: chatData.response.conversation_id, + slug: chatData.response.slug, + } + setData(chatMetadata); + } + setHasMoreMessages(false); + setFetchingData(false); } }) .catch(err => { @@ -241,7 +252,6 @@ export default function ChatHistory(props: ChatHistoryProps) { if (!props.conversationId && !props.publicConversationSlug) { return null; } - return (
@@ -334,7 +344,7 @@ export default function ChatHistory(props: ChatHistoryProps) { } + avatar={} description={constructAgentPersona()} />
diff --git a/src/interface/web/app/components/chatInputArea/chatInputArea.tsx b/src/interface/web/app/components/chatInputArea/chatInputArea.tsx index e2b59848..e1f546f9 100644 --- a/src/interface/web/app/components/chatInputArea/chatInputArea.tsx +++ b/src/interface/web/app/components/chatInputArea/chatInputArea.tsx @@ -63,6 +63,29 @@ interface ChatInputProps { isLoggedIn: boolean; } +async function createNewConvo() { + try { + const response = await fetch('/api/chat/sessions', { method: "POST" }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + const conversationID = data.conversation_id; + + if (!conversationID) { + throw new Error("Conversation ID not found in response"); + } + + const url = `/chat?conversationId=${conversationID}`; + return url; + } catch (error) { + console.error("Error creating new conversation:", error); + throw error; + } +} + export default function ChatInputArea(props: ChatInputProps) { const [message, setMessage] = useState(''); const fileInputRef = useRef(null); @@ -270,7 +293,7 @@ export default function ChatInputArea(props: ChatInputProps) {
} -
+
- +