diff --git a/src/app/api/chat/route.ts b/src/app/api/chat/route.ts index c63b88b..eb3f48e 100644 --- a/src/app/api/chat/route.ts +++ b/src/app/api/chat/route.ts @@ -12,15 +12,6 @@ const openai = new OpenAIApi(config) // IMPORTANT! Set the runtime to edge export const runtime = 'edge' -export class ExtendedStreamingTextResponse extends StreamingTextResponse { - additionalData: any; - - constructor(stream: ReturnType, additionalData: any) { - super(stream); - this.additionalData = additionalData; - } -} - export async function POST(req: Request) { try { diff --git a/src/app/appContext.tsx b/src/app/appContext.tsx new file mode 100644 index 0000000..a2fb14a --- /dev/null +++ b/src/app/appContext.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + + +interface RefreshIndexContextType { + totalRecords: number; + refreshIndex: () => Promise; +} +const defaultContext: RefreshIndexContextType = { + totalRecords: 0, + refreshIndex: () => Promise.resolve(), +}; + +const AppContext = React.createContext(defaultContext); + +export default AppContext; \ No newline at end of file diff --git a/src/app/components/Chat/ChatInput.tsx b/src/app/components/Chat/ChatInput.tsx index bcaef84..5bf1faf 100644 --- a/src/app/components/Chat/ChatInput.tsx +++ b/src/app/components/Chat/ChatInput.tsx @@ -1,3 +1,4 @@ +import { UpArrowSvg } from '@/utils/svg/upArrow'; import React, { FormEvent } from 'react'; interface ChatInputProps { @@ -18,11 +19,13 @@ const styles = { const ChatInput: React.FC = ({ input, handleInputChange, handleMessageSubmit, showIndexMessage }) => { return (
+
+ = ({ input, handleInputChange, handleM maxLength={105} />
0 ? "visible" : "hidden"}` }}>Hit enter to send
- - - +
{showIndexMessage &&
diff --git a/src/app/components/Chat/ChatWrapper.tsx b/src/app/components/Chat/ChatWrapper.tsx index 8446321..591805e 100644 --- a/src/app/components/Chat/ChatWrapper.tsx +++ b/src/app/components/Chat/ChatWrapper.tsx @@ -32,8 +32,6 @@ const Chat: React.FC = forwardRef(({ withCo } }, [data, setContext]); - const prevMessagesLengthRef = useRef(messages.length); - const chatRef = useRef(null); useImperativeHandle(ref, () => ({ diff --git a/src/app/components/Chat/Messages.tsx b/src/app/components/Chat/Messages.tsx index 4d25cb3..1d3f7aa 100644 --- a/src/app/components/Chat/Messages.tsx +++ b/src/app/components/Chat/Messages.tsx @@ -23,18 +23,6 @@ export default function Messages({ messages, withContext, context }: { messages: setAnchorEls(prev => ({ ...prev, [messageId]: null })); }; - // const open = Boolean(anchorEl); - // const id = open ? 'simple-popover' : undefined; - - const assistantMessages = messages.filter((_, index) => index % 2 === 1); - const assistantMessagesObject = assistantMessages.reduce((obj: { [key: string]: Message }, message) => { - obj[message.id] = message; - return obj; - }, {}); - - console.log(assistantMessagesObject); - console.log("context", context); - const styles = { lightGrey: { color: "#72788D" diff --git a/src/app/components/Chat/index.tsx b/src/app/components/Chat/index.tsx index e8d288e..4058239 100644 --- a/src/app/components/Chat/index.tsx +++ b/src/app/components/Chat/index.tsx @@ -1,20 +1,28 @@ -import React, { FormEvent, ChangeEvent, useRef } from "react"; +import React, { FormEvent, ChangeEvent, useRef, useEffect, useContext } from "react"; import ChatWrapper, { ChatInterface } from "./ChatWrapper"; import ChatInput from "./ChatInput"; import { Message } from "ai"; import type { PineconeRecord, RecordMetadata, ScoredPineconeRecord } from "@pinecone-database/pinecone"; +import useRefreshIndex from "@/hooks/useRefreshIndex"; +import AppContext from "@/appContext"; interface ChatProps { setContext: (data: { context: PineconeRecord[] }[]) => void; - showIndexMessage: boolean; context: { context: PineconeRecord[] }[] | null; } -const Chat: React.FC = ({ setContext, showIndexMessage, context }) => { +const Chat: React.FC = ({ setContext, context }) => { const chatWithContextRef = useRef(null); const chatWithoutContextRef = useRef(null); + const { totalRecords } = useContext(AppContext); + + useEffect(() => { + console.log("totalRecords", totalRecords) + }, [totalRecords]) + + const [input, setInput] = React.useState("") const onMessageSubmit = (e: FormEvent) => { setInput("") @@ -41,7 +49,7 @@ const Chat: React.FC = ({ setContext, showIndexMessage, context }) =>
- +
); diff --git a/src/app/components/Context/Button.tsx b/src/app/components/Sidebar/Button.tsx similarity index 100% rename from src/app/components/Context/Button.tsx rename to src/app/components/Sidebar/Button.tsx diff --git a/src/app/components/Context/Card.tsx b/src/app/components/Sidebar/Card.tsx similarity index 100% rename from src/app/components/Context/Card.tsx rename to src/app/components/Sidebar/Card.tsx diff --git a/src/app/components/Sidebar/RecursiveSplittingOptions.tsx b/src/app/components/Sidebar/RecursiveSplittingOptions.tsx new file mode 100644 index 0000000..351247c --- /dev/null +++ b/src/app/components/Sidebar/RecursiveSplittingOptions.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import { Popover, PopoverHandler, PopoverContent } from "@material-tailwind/react"; +import { IoMdInformationCircleOutline } from "react-icons/io"; + +interface RecursiveSplittingOptionsProps { + chunkSize: number; + setChunkSize: (value: number) => void; + overlap: number; + setOverlap: (value: number) => void; + openChunkSizePopover: boolean; + setOpenChunkSizePopover: (value: boolean) => void; + openOverLapPopover: boolean; + setOpenOverLapPopover: (value: boolean) => void; +} + +export const RecursiveSplittingOptions: React.FC = ({ + chunkSize, + setChunkSize, + overlap, + setOverlap, + openChunkSizePopover, + setOpenChunkSizePopover, + openOverLapPopover, + setOpenOverLapPopover +}) => { + const chunkSizePopoverTriggers = { + onMouseEnter: () => setOpenChunkSizePopover(true), + onMouseLeave: () => setOpenChunkSizePopover(false), + }; + + const overLapPopoverTriggers = { + onMouseEnter: () => setOpenOverLapPopover(true), + onMouseLeave: () => setOpenOverLapPopover(false), + }; + + return ( +
+
+
+
+ Chunk Size: {chunkSize} +
+ + +
+
+ +
+ Chunk size in recursive text splitting is the user defined portion of text that's divided and processed in each recursion step, influencing the accuracy of the operation. +
+
+
+
+
+
+ + setChunkSize(parseInt(e.target.value))} + /> +
+
+
+ Overlap:{overlap} +
+ + +
+
+ +
+ Overlap in recursive text splitting is the user-specified section of text that's intentionally repeated across chunks to maintain context and potentially enhance accuracy. +
+
+
+
+
+
+ setOverlap(parseInt(e.target.value))} + /> +
+
+
+ ); +}; \ No newline at end of file diff --git a/src/app/components/Context/UrlButton.tsx b/src/app/components/Sidebar/UrlButton.tsx similarity index 100% rename from src/app/components/Context/UrlButton.tsx rename to src/app/components/Sidebar/UrlButton.tsx diff --git a/src/app/components/Context/index.tsx b/src/app/components/Sidebar/index.tsx similarity index 51% rename from src/app/components/Context/index.tsx rename to src/app/components/Sidebar/index.tsx index d7deb4f..f28b014 100644 --- a/src/app/components/Context/index.tsx +++ b/src/app/components/Sidebar/index.tsx @@ -1,31 +1,14 @@ -import React, { createRef, useEffect, useRef, useState } from "react"; -import { urls } from "./urls"; -import { Card, ICard } from "./Card"; -import { clearIndex, crawlDocument } from "./utils"; -import Select, { SelectChangeEvent } from '@mui/material/Select'; -import MenuItem from '@mui/material/MenuItem'; +import AppContext from "@/appContext"; +import { Button } from "@material-tailwind/react"; import CircularProgress from '@mui/material/CircularProgress'; -// import { Button } from "./Button"; +import MenuItem from '@mui/material/MenuItem'; +import Select, { SelectChangeEvent } from '@mui/material/Select'; +import React, { useContext, useState } from "react"; import Header from "../Header"; -import type { PineconeRecord } from "@pinecone-database/pinecone"; -import { IoMdInformationCircleOutline } from "react-icons/io"; -// import Popover from "@mui/material/Popover"; -import { Typography } from "@mui/material"; - - -import { - Popover, - PopoverHandler, - PopoverContent, - Button - -} from "@material-tailwind/react"; - -interface ContextProps { - className: string; - context: { context: PineconeRecord[] }[] | null; - refreshIndex: () => void; -} +import { ICard } from "./Card"; +import { RecursiveSplittingOptions } from "./RecursiveSplittingOptions"; +import { urls } from "./urls"; +import { clearIndex, crawlDocument } from "./utils"; const styles = { contextWrapper: { @@ -56,9 +39,7 @@ const styles = { } - - -export const Context: React.FC = ({ className, context, refreshIndex }) => { +export const Sidebar: React.FC = () => { const [entries, setEntries] = useState(urls); const [cards, setCards] = useState([]); const [splittingMethod, setSplittingMethod] = useState("markdown"); @@ -68,23 +49,10 @@ export const Context: React.FC = ({ className, context, refreshInd const [clearIndexComplete, setClearIndexCompleteMessageVisible] = useState(false) const [crawling, setCrawling] = useState(false) const [crawlingDoneVisible, setCrawlingDoneVisible] = useState(false) + const [openChunkSizePopover, setOpenChunkSizePopover] = useState(false); + const [openOverLapPopover, setOpenOverLapPopover] = useState(false); - const [anchorElChunkSize, setAnchorElChunkSize] = useState(null); - const [anchorElOverlap, setAnchorElOverlap] = useState(null); - - const chunkSizeOpen = Boolean(anchorElChunkSize); - const overlapOpen = Boolean(anchorElOverlap); - - - - - const DropdownLabel: React.FC< - React.PropsWithChildren<{ htmlFor: string }> - > = ({ htmlFor, children }) => ( - - ); + const { refreshIndex } = useContext(AppContext); const handleUrlChange = (event: SelectChangeEvent) => { const { @@ -100,6 +68,19 @@ export const Context: React.FC = ({ className, context, refreshInd setSplittingMethod(value) } + + const chunkSizePopoverTriggers = { + onMouseEnter: () => setOpenChunkSizePopover(true), + onMouseLeave: () => setOpenChunkSizePopover(false), + }; + + + + const overLapPopoverTriggers = { + onMouseEnter: () => setOpenOverLapPopover(true), + onMouseLeave: () => setOpenOverLapPopover(false), + }; + const handleEmbedAndUpsertClick = async () => { setCrawling(true) await crawlDocument( @@ -115,6 +96,7 @@ export const Context: React.FC = ({ className, context, refreshInd setCrawlingDoneVisible(true) setTimeout(() => { setCrawlingDoneVisible(false) + console.log("it's time") refreshIndex() }, 2000) } @@ -128,24 +110,6 @@ export const Context: React.FC = ({ className, context, refreshInd }, 2000) } - - const handleChunkSizePopoverOpen = (event: React.MouseEvent) => { - console.log(infoIconRef.current) - setAnchorElChunkSize(infoIconRef.current); - }; - - const handleChunkSizePopoverClose = () => { - setAnchorElChunkSize(null); - }; - - const handleOverlapPopoverOpen = (event: React.MouseEvent) => { - setAnchorElOverlap(event.currentTarget); - }; - - const handleOverlapPopoverClose = () => { - setAnchorElOverlap(null); - }; - const buttons = entries.map((entry, key) => ( = ({ className, context, refreshInd )); - const [openChunkSizePopover, setOpenChunkSizePopover] = useState(false); - - const chunkSizePopoverTriggers = { - onMouseEnter: () => setOpenChunkSizePopover(true), - onMouseLeave: () => setOpenChunkSizePopover(false), - }; - - const [openOverLapPopover, setOpenOverLapPopover] = useState(false); - - const overLapPopoverTriggers = { - onMouseEnter: () => setOpenOverLapPopover(true), - onMouseLeave: () => setOpenOverLapPopover(false), - }; return (
= ({ className, context, refreshInd
{splittingMethod === "recursive" && ( -
-
-
-
- - Chunk Size: {chunkSize} -
- - -
-
- -
- Chunk size in recursive text splitting is the user defined portion of text that's divided and processed in each recursion step, influencing the accuracy of the operation. -
-
-
-
-
-
- - setChunkSize(parseInt(e.target.value))} - /> -
-
- -
- Overlap:{overlap} -
-
- - -
-
- -
- Overlap in recursive text splitting is the user-specified section of text that's intentionally repeated across chunks to maintain context and potentially enhance accuracy. -
-
-
-
-
-
-
-
- setOverlap(parseInt(e.target.value))} - /> -
-
-
+ )} -
+
Index records
-
Clear
+
Clear
{( -
+
Index cleared
)} {( -
+
Chunking and embedding your data... @@ -306,8 +217,8 @@ export const Context: React.FC = ({ className, context, refreshInd
{cards && cards.length > 0 ? -
-
{cards.length} results
+
+
{cards.length} results
@@ -316,10 +227,6 @@ export const Context: React.FC = ({ className, context, refreshInd
}
- {/* {cards && - cards.map((card, key) => ( - - ))} */}
diff --git a/src/app/components/Context/urls.ts b/src/app/components/Sidebar/urls.ts similarity index 100% rename from src/app/components/Context/urls.ts rename to src/app/components/Sidebar/urls.ts diff --git a/src/app/components/Context/utils.ts b/src/app/components/Sidebar/utils.ts similarity index 100% rename from src/app/components/Context/utils.ts rename to src/app/components/Sidebar/utils.ts diff --git a/src/app/hooks/clearIndex.ts b/src/app/hooks/clearIndex.ts deleted file mode 100644 index dabb77a..0000000 --- a/src/app/hooks/clearIndex.ts +++ /dev/null @@ -1,34 +0,0 @@ -// import { useState, useEffect } from 'react'; - -// export const useClearIndex = (setEntries: React.Dispatch>, setCards: React.Dispatch>) => { -// const [isLoading, setIsLoading] = useState(false); -// const [error, setError] = useState(null); -// const [trigger, setTrigger] = useState(0); - -// useEffect(() => { -// const clearIndex = async () => { -// setIsLoading(true); -// try { -// const response = await fetch("/api/clearIndex", { -// method: "POST", -// headers: { "Content-Type": "application/json" }, -// }); -// if (!response.ok) { -// throw new Error('Network response was not ok'); -// } -// const data = await response.json(); -// setEntries(data.entries); -// setCards(data.cards); -// } catch (error) { -// setError(error); -// } finally { -// setIsLoading(false); -// } -// }; -// clearIndex(); -// }, [trigger]); - -// return { isLoading, error, trigger, setTrigger }; -// }; - - diff --git a/src/app/hooks/useRefreshIndex.ts b/src/app/hooks/useRefreshIndex.ts new file mode 100644 index 0000000..14f6a67 --- /dev/null +++ b/src/app/hooks/useRefreshIndex.ts @@ -0,0 +1,21 @@ +import { useState } from 'react'; + +const useRefreshIndex = () => { + const [totalRecords, setTotalRecords] = useState(0); + + const refreshIndex = async () => { + const response = await fetch("/api/checkIndex", { + method: "POST", + }); + try { + const stats = await response.json(); + setTotalRecords(stats.totalRecordCount); + } catch (e) { + console.log(e) + } + } + + return { totalRecords, refreshIndex }; +} + +export default useRefreshIndex; \ No newline at end of file diff --git a/src/app/page.tsx b/src/app/page.tsx index bb0b62a..ddc87d4 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,59 +2,36 @@ "use client"; -import React, { useEffect, useRef, useState, FormEvent } from "react"; -import { Context } from "@/components/Context"; +import React, { useEffect, useState } from "react"; +import { Sidebar } from "@/components/Sidebar"; import Chat from "@/components/Chat"; -import InstructionModal from "./components/InstructionModal"; -import { Message } from "ai"; -import { PineconeRecord, ScoredPineconeRecord } from "@pinecone-database/pinecone"; +import AppContext from "./appContext"; +import useRefreshIndex from '@/hooks/useRefreshIndex' +import type { PineconeRecord } from "@pinecone-database/pinecone"; const Page: React.FC = () => { const [context, setContext] = useState<{ context: PineconeRecord[] }[] | null>(null); - const [isModalOpen, setModalOpen] = useState(false); + const { totalRecords, refreshIndex } = useRefreshIndex(); - const [totalRecords, setTotalRecords] = useState(0); - - - const refreshIndex = async () => { - const response = await fetch("/api/checkIndex", { - method: "POST", - }); - try { - const stats = await response.json(); - setTotalRecords(stats.totalRecordCount); - } catch (e) { - console.log(e) + useEffect(() => { + if (totalRecords === 0) { + refreshIndex() } - } - + }, [refreshIndex, totalRecords]) return ( -
- setModalOpen(false)} - /> -
-
- + +
+
+
+ +
+
- -
-
+ ); }; diff --git a/src/app/utils/svg/pinecone.tsx b/src/app/utils/svg/pinecone.tsx index 58de5d1..81572f6 100644 --- a/src/app/utils/svg/pinecone.tsx +++ b/src/app/utils/svg/pinecone.tsx @@ -1,5 +1,5 @@ export const PineconeSvg = () => { - return + return diff --git a/src/app/utils/svg/pineconeLogo.tsx b/src/app/utils/svg/pineconeLogo.tsx index 1bf960e..c389ebf 100644 --- a/src/app/utils/svg/pineconeLogo.tsx +++ b/src/app/utils/svg/pineconeLogo.tsx @@ -1,5 +1,5 @@ export const PineconeLogoSvg = () => { - return ( + return ( @@ -8,6 +8,6 @@ export const PineconeLogoSvg = () => { - + ) } \ No newline at end of file diff --git a/src/app/utils/svg/upArrow.tsx b/src/app/utils/svg/upArrow.tsx new file mode 100644 index 0000000..bdd73b5 --- /dev/null +++ b/src/app/utils/svg/upArrow.tsx @@ -0,0 +1,5 @@ +export const UpArrowSvg = () => { + return + + +} \ No newline at end of file