"use client"; import { useChat } from "ai/react"; import { useEffect, useRef, useState } from "react"; import Markdown from "react-markdown"; import { Loader2, MessagesSquare, PanelLeftClose, PanelRightClose } from "lucide-react"; import { ArrowUpIcon, XMarkIcon } from "@heroicons/react/24/outline"; import { HermesAIBotIcon, NoteBookIcon, StarLockIcon, WorldIcon } from "app/ai-bot/icons/icon-svg"; import { useSelector } from "react-redux"; import { optimizerData } from "../../../store/app/appSelector"; import ChatHistoryPanel from './ChatHistoryPanel'; import { useChatHistory } from "../hooks/useChatHistory"; import { formatSystemMessage } from "../utils/messageFormatter"; import { WelcomeScreen } from "./WelcomeScreen"; import { useAppContext } from "../../../context/app"; import { isDev } from "lib/utils"; import { AnimatedLogo } from "components/animated-logo"; interface ChatProps { apiEndpoint?: string; } const aiRoot = window.rapidload_optimizer.ai_root || "https://ai.rapidload.io/api"; export default function Chat({ apiEndpoint = `${aiRoot}/support` }: ChatProps) { const { options } = useAppContext(); const [showHistory, setShowHistory] = useState(false); const [error, setError] = useState(null); const { messages, input, handleInputChange, handleSubmit, setMessages, isLoading } = useChat({ api: apiEndpoint, headers: { 'Authorization': `Bearer ${isDev ? import.meta.env.VITE_KEY : options.license_key!}` }, body: { 'url': options?.optimizer_url || '' }, onError: (error) => { console.error(error); const lastUserMessage = [...messages].reverse().find(msg => msg.role === 'user'); // Use a fallback ID if no user message exists const errorId = lastUserMessage?.id || 'system'; setError(`${errorId}:${error?.message}`); }, onFinish: (message, options) => { // console.log(message, options); } }); const { data, settings, activeReport, activeGear, testMode, license } = useSelector(optimizerData); const messagesEndRef = useRef(null); const { conversations, handleNewChat, handleSelectConversation, handleDeleteConversation, } = useChatHistory(messages, setMessages); // Scroll to bottom when messages change // useEffect(() => { // messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); // }, [messages]); // Initialize system message useEffect(() => { const systemMessage = formatSystemMessage({ data, settings, license, activeReport, activeGear: activeGear as string, testMode: testMode as boolean, }); setMessages([ { id: "1", role: "system", content: systemMessage, createdAt: new Date(), }, ]); }, [data, settings, license, activeReport, activeGear, testMode, setMessages]); // Handle URL parameters for conversation selection useEffect(() => { const params = new URLSearchParams(window.location.hash.split('?')[1]); const conversationId = params.get('conv'); if (conversationId) { const conversation = conversations.find(conv => conv.id === conversationId); if (conversation) { handleSelectConversation(conversationId); } } else { // handleNewChat(true); // // Initialize system message first // const systemMessage = formatSystemMessage({ // data, // settings, // license, // activeReport, // activeGear: activeGear as string, // testMode: testMode as boolean, // }); // setMessages([ // { // id: "1", // role: "system", // content: systemMessage, // createdAt: new Date(), // }, // ]); } }, []); // Add this new function const handleClose = () => { // Remove empty conversations before closing conversations.forEach(conv => { if (conv.messages.length <= 1) { // Only has system message or is empty handleDeleteConversation(conv.id); } }); window.location.hash = '#/'; }; // Add this new function to handle form submission const handleFormSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(null); // Prevent multiple submissions while processing if (isLoading) return; // If there are no messages except system message, create a new chat if (messages.length <= 1) { await handleNewChat(true); await new Promise(resolve => setTimeout(resolve, 100)); } handleSubmit(e); }; return (
handleNewChat(false)} onDeleteConversation={handleDeleteConversation} />
{messages.length === 1 ? ( ) : ( )}
); } // Extracted Components // const ChatMessages = ({ messages }: { messages: any[] }) => ( // <> // {messages // .filter((msg) => msg.role !== "system") // .map((message) => ( // // ))} // // ); // const ChatMessages = ({ messages, loading, error }: { messages: any[], loading: boolean, error: string | null }) => ( // <> // {Array.isArray(messages) && messages // .filter((msg) => msg && typeof msg === 'object' && msg?.role !== "system") // .map((message, index) => message && ( //
// // {index === messages.length - 2 && loading && ( //
//
// //
//
// Thinking... //
//
// )} //
// ))} // // ); // working with erro chat // const ChatMessages = ({ messages, loading, error }: { messages: any[], loading: boolean, error: string | null }) => { // const filteredMessages = messages.filter((msg) => msg && typeof msg === 'object' && msg?.role !== "system"); // // Parse error to get message ID and error text // const [errorMessageId, errorText] = error ? error.split(':') : [null, null]; // return ( // <> // {Array.isArray(messages) && filteredMessages.map((message, index) => message && ( //
// // {index === filteredMessages.length - 2 && loading && ( //
//
// //
//
// Thinking... //
//
// )} //
// ))} // // ); // }; const ChatMessages = ({ messages, loading, error }: { messages: any[], loading: boolean, error: string | null }) => { const filteredMessages = messages.filter((msg) => msg && typeof msg === 'object' && msg?.role !== "system"); const [errorMessageId, errorText] = error ? error.split(':') : [null, null]; console.log(errorMessageId, errorText); return ( <> {Array.isArray(messages) && filteredMessages.map((message, index) => { const isErrorMessage = message.id === errorMessageId || (errorMessageId === 'system' && index === 0); const nextMessage = filteredMessages[index + 1]; return (
{/* Show loading state */} {index === filteredMessages.length - 1 && loading && message.role === 'user' && (
Thinking...
)} {/* Show error after the user message that triggered it */} {isErrorMessage && error && !loading && (
{errorText}
)}
); })} ); }; const ChatMessage = ({ message, loading }: { message: any, loading: boolean }) => { if (!message) return null; return (
{message.role === "assistant" && (
)}
{message.content && message.content.length > 0 ? (
{message.content}
) : ( {message?.toolInvocations?.[0]?.toolName === 'accessKnowledge' && (
Accessing knowledge base...
)}
)}
{/* Show error message if this is the last message */} {/* {error && message.role === "user" && (
{error}
)} */}
); }; const MessageTimestamp = ({ createdAt }: { createdAt: any }) => (
{new Date(createdAt || Date.now()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", })}
); const ChatInput = ({ input, handleInputChange, handleSubmit }: { input: any, handleInputChange: any, handleSubmit: any }) => (
);