diff --git a/package-lock.json b/package-lock.json index 7e24fcb..45d6166 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "vercel-pinecone-template", "version": "0.1.0", "dependencies": { - "@pinecone-database/doc-splitter": "0.0.1", + "@pinecone-database/doc-splitter": "^0.0.1", "@pinecone-database/pinecone": "^0.1.6", "@types/node": "20.4.0", "@types/react": "18.2.14", @@ -23,8 +23,10 @@ "openai-edge": "^1.2.0", "react": "18.2.0", "react-dom": "18.2.0", + "react-icons": "^4.10.1", "react-markdown": "^8.0.7", - "svelte": "^4.0.5", + "sswr": "^2.0.0", + "svelte": "^3.29.0", "tailwindcss": "3.3.2", "typescript": "5.1.6", "unified": "^10.1.2", @@ -53,18 +55,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/parser": { "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.6.tgz", @@ -397,6 +387,14 @@ "node": ">= 8" } }, + "node_modules/@pinecone-database/doc-splitter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@pinecone-database/doc-splitter/-/doc-splitter-0.0.1.tgz", + "integrity": "sha512-UCJgNZKnXPcrTblI9ui6H+HbQbyYf8bsGQAGR3f4IYW2Lw1zQ0izFrjGsCgqiP+GE837JXv43+qgkSqwWlnZtA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@pinecone-database/pinecone": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@pinecone-database/pinecone/-/pinecone-0.1.6.tgz", @@ -440,11 +438,6 @@ "@types/ms": "*" } }, - "node_modules/@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" - }, "node_modules/@types/hast": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", @@ -778,6 +771,22 @@ } } }, + "node_modules/ai/node_modules/sswr": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/sswr/-/sswr-1.11.0.tgz", + "integrity": "sha512-D7nEdEH0A1TKZjA1wRP6frxw/dtTCuV70EK5/pELP+FrcPmyaP8jLC49mrg86Ez5cb5z3NQb2MPx2M7ZZlSupQ==", + "dependencies": { + "swrev": "^3.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0" + } + }, + "node_modules/ai/node_modules/swrev": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/swrev/-/swrev-3.0.0.tgz", + "integrity": "sha512-QJuZiptdOmbDY45pECBRVEgnoBlOKjeT2MWVz04wKHpWX15hM3P7EjcIbHDg5yLoPCMQ7to3349MEE+l9QF5HA==" + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1214,18 +1223,6 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, - "node_modules/code-red": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.3.tgz", - "integrity": "sha512-kVwJELqiILQyG5aeuyKFbdsI1fmQy1Cmf7dQ8eGmVuJoaRVdwey7WaMknr2ZFeVSYSKT0rExsa8EGw0aoI/1QQ==", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.14", - "@types/estree": "^1.0.0", - "acorn": "^8.8.2", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1300,18 +1297,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -2071,14 +2056,6 @@ "node": ">=4.0" } }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2825,14 +2802,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-reference": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.1.tgz", - "integrity": "sha512-baJJdQLiYaJdvFbJqXrcGv3WU3QCzBlUcI5QhbesIm6/xPsvmO+2CDoi/GMOFBQEQm+PXkwOPrp9KK5ozZsp2w==", - "dependencies": { - "@types/estree": "*" - } - }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -3062,11 +3031,6 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, - "node_modules/locate-character": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", - "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==" - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3202,11 +3166,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4160,16 +4119,6 @@ "node": ">=8" } }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -4375,6 +4324,14 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz", + "integrity": "sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -4766,11 +4723,11 @@ } }, "node_modules/sswr": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/sswr/-/sswr-1.11.0.tgz", - "integrity": "sha512-D7nEdEH0A1TKZjA1wRP6frxw/dtTCuV70EK5/pELP+FrcPmyaP8jLC49mrg86Ez5cb5z3NQb2MPx2M7ZZlSupQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sswr/-/sswr-2.0.0.tgz", + "integrity": "sha512-mV0kkeBHcjcb0M5NqKtKVg/uTIYNlIIniyDfSGrSfxpEdM9C365jK0z55pl9K0xAkNTJi2OAOVFQpgMPUk+V0w==", "dependencies": { - "swrev": "^3.0.0" + "swrev": "^4.0.0" }, "peerDependencies": { "svelte": "^4.0.0" @@ -4986,26 +4943,11 @@ } }, "node_modules/svelte": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.0.5.tgz", - "integrity": "sha512-PHKPWP1wiWHBtsE57nCb8xiWB3Ht7/3Kvi3jac0XIxUM2rep8alO7YoAtgWeGD7++tFy46krilOrPW0mG3Dx+A==", - "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^3.2.1", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", - "locate-character": "^3.0.0", - "magic-string": "^0.30.0", - "periscopic": "^3.1.0" - }, + "version": "3.59.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.2.tgz", + "integrity": "sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==", "engines": { - "node": ">=16" + "node": ">= 8" } }, "node_modules/swr": { @@ -5020,9 +4962,9 @@ } }, "node_modules/swrev": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/swrev/-/swrev-3.0.0.tgz", - "integrity": "sha512-QJuZiptdOmbDY45pECBRVEgnoBlOKjeT2MWVz04wKHpWX15hM3P7EjcIbHDg5yLoPCMQ7to3349MEE+l9QF5HA==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/swrev/-/swrev-4.0.0.tgz", + "integrity": "sha512-LqVcOHSB4cPGgitD1riJ1Hh4vdmITOp+BkmfmXRh4hSF/t7EnS4iD+SOTmq7w5pPm/SiPeto4ADbKS6dHUDWFA==" }, "node_modules/swrv": { "version": "1.0.3", diff --git a/src/app/components/Card/index.tsx b/src/app/components/Card/index.tsx new file mode 100644 index 0000000..5ed03fc --- /dev/null +++ b/src/app/components/Card/index.tsx @@ -0,0 +1,54 @@ +import React, { FC } from "react"; +import ReactMarkdown from "react-markdown"; + +export interface ICard { + pageContent: string; + score?: number; + values?: any[]; + metadata: { + hash: string; + text?: string; + url?: string; + }; +} + +interface ICardProps { + card: ICard; + selected?: string[] | null; +} + +export const Card: FC = ({ card, selected }) => { + const relevancePercentage = card.score ? (card.score * 100).toFixed(0) : undefined; + + return ( +
+ {/* Display the URL as a source link if it exists */} + {card.metadata.url && ( +
+ Source: + + {card.metadata.url} + +
+ )} + + {/* Always display the page content */} + {card.pageContent} + + {/* Display the relevance percentage if score exists */} + {relevancePercentage &&
{relevancePercentage}% relevant
} + + {/* Display the hash */} + {card.metadata.hash} +
+ ); +}; diff --git a/src/app/components/Context/Card.tsx b/src/app/components/Context/Card.tsx deleted file mode 100644 index 2be8e32..0000000 --- a/src/app/components/Context/Card.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { FC } from "react"; -import ReactMarkdown from "react-markdown"; - -export interface ICard { - pageContent: string; - metadata: { - hash: string; - }; -} - -interface ICardProps { - card: ICard; - selected: string[] | null; -} - -export const Card: FC = ({ card, selected }) => ( -
- {card.pageContent} - {card.metadata.hash} -
-); diff --git a/src/app/components/Context/index.tsx b/src/app/components/Context/index.tsx index afc73bd..50a6073 100644 --- a/src/app/components/Context/index.tsx +++ b/src/app/components/Context/index.tsx @@ -1,7 +1,8 @@ import React, { ChangeEvent, useCallback, useEffect, useState } from "react"; import { urls } from "./urls"; import UrlButton from "./UrlButton"; -import { Card, ICard } from "./Card"; +import { Card, ICard } from "@/components/Card"; + import { clearIndex, crawlDocument } from "./utils"; import { Button } from "./Button"; diff --git a/src/app/components/RelevantDocs/index.tsx b/src/app/components/RelevantDocs/index.tsx new file mode 100644 index 0000000..43d95d5 --- /dev/null +++ b/src/app/components/RelevantDocs/index.tsx @@ -0,0 +1,39 @@ +import React, { FC } from "react"; +import { Card, ICard } from "@/components/Card"; + + +export interface IRelevantDoc { + id: string; + score: number; + values: any[]; + metadata: { + chunk: string; + hash: string; + text: string; + url: string; + }; +} + +interface RelevantDocsProps { + relevantDocs: IRelevantDoc[] | null; +} +export const RelevantDocs: FC = ({ relevantDocs }) => { + return ( +
+ {relevantDocs && relevantDocs.map((doc) => { + const cardData: ICard = { + pageContent: doc.metadata.chunk, + metadata: { + hash: doc.metadata.hash, + text: doc.metadata.text, + url: doc.metadata.url + }, + score: doc.score, + values: doc.values + }; + return ; + })} +
+ ); +}; + diff --git a/src/app/components/SidePanel/index.tsx b/src/app/components/SidePanel/index.tsx new file mode 100644 index 0000000..92e6a72 --- /dev/null +++ b/src/app/components/SidePanel/index.tsx @@ -0,0 +1,47 @@ +import React, { useState } from "react"; +import { Context } from "@/components/Context"; +import { RelevantDocs, IRelevantDoc } from "@/components/RelevantDocs"; + +interface SidePanelProps { + context: string[] | null; + relevantDocs: IRelevantDoc[] | null; +} + +const SidePanel: React.FC = ({ context, relevantDocs }) => { + const [activePanel, setActivePanel] = useState<"context" | "relevantDocs">("context"); + + const buttonBaseStyles = "py-2 px-4 w-full my-2 uppercase active:scale-[98%] transition-transform duration-100 rounded"; + const inactiveButtonStyles = "bg-#4f6574 text-white"; + const activeButtonStyles = "bg-gray-300 hover:bg-gray-400"; + + return ( +
+ {/* Toggle Buttons */} +
+ + +
+ + {/* Panels */} + {activePanel === "context" && ( + + )} + + {activePanel === "relevantDocs" && ( + + )} +
+ ); +}; + +export default SidePanel; diff --git a/src/app/page.tsx b/src/app/page.tsx index 41d8de4..fa13852 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -3,16 +3,17 @@ "use client"; import React, { useEffect, useRef, useState, FormEvent } from "react"; -import { Context } from "@/components/Context"; import Header from "@/components/Header"; import Chat from "@/components/Chat"; import { useChat } from "ai/react"; import InstructionModal from "./components/InstructionModal"; import { AiFillGithub, AiOutlineInfoCircle } from "react-icons/ai"; +import SidePanel from "./components/SidePanel"; const Page: React.FC = () => { const [gotMessages, setGotMessages] = useState(false); const [context, setContext] = useState(null); + const [relevantDocs, setRelevantDocs] = useState<[] | null>(null); const [isModalOpen, setModalOpen] = useState(false); const { messages, input, handleInputChange, handleSubmit } = useChat({ @@ -27,6 +28,7 @@ const Page: React.FC = () => { e.preventDefault(); handleSubmit(e); setContext(null); + setRelevantDocs(null); setGotMessages(false); }; @@ -39,6 +41,7 @@ const Page: React.FC = () => { }), }); const { context } = await response.json(); + setRelevantDocs(context); setContext(context.map((c: any) => c.id)); }; if (gotMessages && messages.length >= prevMessagesLengthRef.current) { @@ -89,7 +92,7 @@ const Page: React.FC = () => { messages={messages} />
- +