Shreyas094's picture
Upload 528 files
372531f verified
raw
history blame
10.9 kB
"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<ChatBoxSettings>({
report_source: 'web',
report_type: 'research_report',
tone: 'Objective'
});
const [question, setQuestion] = useState("");
const [orderedData, setOrderedData] = useState<Data[]>([]);
const [showHumanFeedback, setShowHumanFeedback] = useState(false);
const [questionForHuman, setQuestionForHuman] = useState<true | false>(false);
const [allLogs, setAllLogs] = useState<any[]>([]);
const chatContainerRef = useRef<HTMLDivElement>(null);
const [isStopped, setIsStopped] = useState(false);
const [showScrollButton, setShowScrollButton] = useState(false);
const mainContentRef = useRef<HTMLDivElement>(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 (
<>
<Header
loading={loading}
isStopped={isStopped}
showResult={showResult}
onStop={handleStopResearch}
onNewResearch={handleStartNewResearch}
/>
<main ref={mainContentRef} className="min-h-[100vh] pt-[120px]">
{!showResult && (
<Hero
promptValue={promptValue}
setPromptValue={setPromptValue}
handleDisplayResult={handleDisplayResult}
/>
)}
{showResult && (
<div className="flex h-full w-full grow flex-col justify-between">
<div className="container w-full space-y-2">
<div className="container space-y-2 task-components">
<ResearchResults
orderedData={orderedData}
answer={answer}
allLogs={allLogs}
chatBoxSettings={chatBoxSettings}
handleClickSuggestion={handleClickSuggestion}
/>
</div>
{showHumanFeedback && (
<HumanFeedback
questionForHuman={questionForHuman}
websocket={socket}
onFeedbackSubmit={handleFeedbackSubmit}
/>
)}
<div className="pt-1 sm:pt-2" ref={chatContainerRef}></div>
</div>
<div id="input-area" className="container px-4 lg:px-0">
{loading ? (
<LoadingDots />
) : (
<InputArea
promptValue={promptValue}
setPromptValue={setPromptValue}
handleSubmit={handleChat}
handleSecondary={handleDisplayResult}
disabled={loading}
reset={reset}
isStopped={isStopped}
/>
)}
</div>
</div>
)}
</main>
{showScrollButton && showResult && (
<button
onClick={scrollToBottom}
className="fixed bottom-8 right-8 flex items-center justify-center w-12 h-12 text-white bg-[rgb(168,85,247)] rounded-full hover:bg-[rgb(147,51,234)] transform hover:scale-105 transition-all duration-200 shadow-lg z-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 14l-7 7m0 0l-7-7m7 7V3"
/>
</svg>
</button>
)}
<Footer setChatBoxSettings={setChatBoxSettings} chatBoxSettings={chatBoxSettings} />
</>
);
}