"use client"; import { useRef, useState, useEffect, useCallback } from "react"; import { useWebSocket } from '@/hooks/useWebSocket'; import { startLanggraphResearch } from '../components/Langgraph/Langgraph'; import findDifferences from '../helpers/findDifferences'; import { Data, ChatBoxSettings, QuestionData } from '../types/data'; import { preprocessOrderedData } from '../utils/dataProcessing'; import { ResearchResults } from '../components/ResearchResults'; import Header from "@/components/Header"; import Hero from "@/components/Hero"; import Footer from "@/components/Footer"; import InputArea from "@/components/ResearchBlocks/elements/InputArea"; import HumanFeedback from "@/components/HumanFeedback"; import LoadingDots from "@/components/LoadingDots"; export default function Home() { const [promptValue, setPromptValue] = useState(""); const [showResult, setShowResult] = useState(false); const [answer, setAnswer] = useState(""); const [loading, setLoading] = useState(false); const [chatBoxSettings, setChatBoxSettings] = useState({ report_source: 'web', report_type: 'research_report', tone: 'Objective' }); const [question, setQuestion] = useState(""); const [orderedData, setOrderedData] = useState([]); const [showHumanFeedback, setShowHumanFeedback] = useState(false); const [questionForHuman, setQuestionForHuman] = useState(false); const [allLogs, setAllLogs] = useState([]); const chatContainerRef = useRef(null); const [isStopped, setIsStopped] = useState(false); const [showScrollButton, setShowScrollButton] = useState(false); const mainContentRef = useRef(null); const { socket, initializeWebSocket } = useWebSocket( setOrderedData, setAnswer, setLoading, setShowHumanFeedback, setQuestionForHuman ); const handleFeedbackSubmit = (feedback: string | null) => { if (socket) { socket.send(JSON.stringify({ type: 'human_feedback', content: feedback })); } setShowHumanFeedback(false); }; const handleChat = async (message: string) => { if (socket) { setShowResult(true); setQuestion(message); setLoading(true); setPromptValue(""); setAnswer(""); const questionData: QuestionData = { type: 'question', content: message }; setOrderedData(prevOrder => [...prevOrder, questionData]); socket.send(`chat${JSON.stringify({ message })}`); } }; const handleDisplayResult = async (newQuestion: string) => { setShowResult(true); setLoading(true); setQuestion(newQuestion); setPromptValue(""); setAnswer(""); setOrderedData((prevOrder) => [...prevOrder, { type: 'question', content: newQuestion }]); const storedConfig = localStorage.getItem('apiVariables'); const apiVariables = storedConfig ? JSON.parse(storedConfig) : {}; const langgraphHostUrl = apiVariables.LANGGRAPH_HOST_URL; if (chatBoxSettings.report_type === 'multi_agents' && langgraphHostUrl) { let { streamResponse, host, thread_id } = await startLanggraphResearch(newQuestion, chatBoxSettings.report_source, langgraphHostUrl); const langsmithGuiLink = `https://smith.langchain.com/studio/thread/${thread_id}?baseUrl=${host}`; setOrderedData((prevOrder) => [...prevOrder, { type: 'langgraphButton', link: langsmithGuiLink }]); let previousChunk = null; for await (const chunk of streamResponse) { if (chunk.data.report != null && chunk.data.report != "Full report content here") { setOrderedData((prevOrder) => [...prevOrder, { ...chunk.data, output: chunk.data.report, type: 'report' }]); setLoading(false); } else if (previousChunk) { const differences = findDifferences(previousChunk, chunk); setOrderedData((prevOrder) => [...prevOrder, { type: 'differences', content: 'differences', output: JSON.stringify(differences) }]); } previousChunk = chunk; } } else { initializeWebSocket(newQuestion, chatBoxSettings); } }; const reset = () => { setShowResult(false); setPromptValue(""); setQuestion(""); setAnswer(""); }; const handleClickSuggestion = (value: string) => { setPromptValue(value); const element = document.getElementById('input-area'); if (element) { element.scrollIntoView({ behavior: 'smooth' }); } }; /** * Handles stopping the current research * - Closes WebSocket connection * - Stops loading state * - Marks research as stopped * - Preserves current results */ const handleStopResearch = () => { if (socket) { socket.close(); } setLoading(false); setIsStopped(true); }; /** * Handles starting a new research * - Clears all previous research data and states * - Resets UI to initial state * - Closes any existing WebSocket connections */ const handleStartNewResearch = () => { // Reset UI states setShowResult(false); setPromptValue(""); setIsStopped(false); // Clear previous research data setQuestion(""); setAnswer(""); setOrderedData([]); setAllLogs([]); // Reset feedback states setShowHumanFeedback(false); setQuestionForHuman(false); // Clean up connections if (socket) { socket.close(); } setLoading(false); }; /** * Processes ordered data into logs for display * Updates whenever orderedData changes */ useEffect(() => { const groupedData = preprocessOrderedData(orderedData); const statusReports = ["agent_generated", "starting_research", "planning_research"]; const newLogs = groupedData.reduce((acc: any[], data) => { // Process accordion blocks (grouped data) if (data.type === 'accordionBlock') { const logs = data.items.map((item: any, subIndex: any) => ({ header: item.content, text: item.output, metadata: item.metadata, key: `${item.type}-${item.content}-${subIndex}`, })); return [...acc, ...logs]; } // Process status reports else if (statusReports.includes(data.content)) { return [...acc, { header: data.content, text: data.output, metadata: data.metadata, key: `${data.type}-${data.content}`, }]; } return acc; }, []); setAllLogs(newLogs); }, [orderedData]); const handleScroll = useCallback(() => { // Calculate if we're near bottom (within 100px) const scrollPosition = window.scrollY + window.innerHeight; const nearBottom = scrollPosition >= document.documentElement.scrollHeight - 100; // Show button if we're not near bottom and page is scrollable const isPageScrollable = document.documentElement.scrollHeight > window.innerHeight; setShowScrollButton(isPageScrollable && !nearBottom); }, []); // Add ResizeObserver to watch for content changes useEffect(() => { const resizeObserver = new ResizeObserver(() => { handleScroll(); }); if (mainContentRef.current) { resizeObserver.observe(mainContentRef.current); } window.addEventListener('scroll', handleScroll); window.addEventListener('resize', handleScroll); return () => { if (mainContentRef.current) { resizeObserver.unobserve(mainContentRef.current); } resizeObserver.disconnect(); window.removeEventListener('scroll', handleScroll); window.removeEventListener('resize', handleScroll); }; }, [handleScroll]); const scrollToBottom = () => { window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' }); }; return ( <>
{!showResult && ( )} {showResult && (
{showHumanFeedback && ( )}
{loading ? ( ) : ( )}
)}
{showScrollButton && showResult && ( )}