From 9aefcce171d3ca49433c124dcde31b694cea02eb Mon Sep 17 00:00:00 2001 From: Kevin Kidd Date: Tue, 23 Jul 2024 18:15:57 -0400 Subject: [PATCH 1/7] feat: add related topics feature and integrate with main page --- app/api/getRelatedTopics/route.ts | 13 ++ app/page.tsx | 322 +++++++++++++++--------------- components/RelatedTopics.tsx | 67 +++++++ 3 files changed, 246 insertions(+), 156 deletions(-) create mode 100644 app/api/getRelatedTopics/route.ts create mode 100644 components/RelatedTopics.tsx diff --git a/app/api/getRelatedTopics/route.ts b/app/api/getRelatedTopics/route.ts new file mode 100644 index 0000000..a4417e2 --- /dev/null +++ b/app/api/getRelatedTopics/route.ts @@ -0,0 +1,13 @@ +import { NextResponse } from "next/server"; + +export async function POST(request: Request) { + const { topic } = await request.json(); + try { + const relatedTopics = [`${topic} 1`, `${topic} 2`]; + return NextResponse.json({ topics: relatedTopics }); + } catch (e) { + return new Response("Error. Failed to generate related topics.", { + status: 202, + }); + } +} diff --git a/app/page.tsx b/app/page.tsx index 32279ca..40897ea 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -6,165 +6,175 @@ import Hero from "@/components/Hero"; import Sources from "@/components/Sources"; import { useState } from "react"; import { - createParser, - ParsedEvent, - ReconnectInterval, + createParser, + type ParsedEvent, + type ReconnectInterval, } from "eventsource-parser"; import { getSystemPrompt } from "@/utils/utils"; import Chat from "@/components/Chat"; +import RelatedTopics from "@/components/RelatedTopics"; export default function Home() { - const [inputValue, setInputValue] = useState(""); - const [topic, setTopic] = useState(""); - const [showResult, setShowResult] = useState(false); - const [sources, setSources] = useState<{ name: string; url: string }[]>([]); - const [isLoadingSources, setIsLoadingSources] = useState(false); - const [messages, setMessages] = useState<{ role: string; content: string }[]>( - [], - ); - const [loading, setLoading] = useState(false); - const [ageGroup, setAgeGroup] = useState("Middle School"); - - const handleInitialChat = async () => { - setShowResult(true); - setLoading(true); - setTopic(inputValue); - setInputValue(""); - - await handleSourcesAndChat(inputValue); - - setLoading(false); - }; - - const handleChat = async (messages?: { role: string; content: string }[]) => { - setLoading(true); - const chatRes = await fetch("/api/getChat", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ messages }), - }); - - if (!chatRes.ok) { - throw new Error(chatRes.statusText); - } - - // This data is a ReadableStream - const data = chatRes.body; - if (!data) { - return; - } - let fullAnswer = ""; - - const onParse = (event: ParsedEvent | ReconnectInterval) => { - if (event.type === "event") { - const data = event.data; - try { - const text = JSON.parse(data).text ?? ""; - fullAnswer += text; - // Update messages with each chunk - setMessages((prev) => { - const lastMessage = prev[prev.length - 1]; - if (lastMessage.role === "assistant") { - return [ - ...prev.slice(0, -1), - { ...lastMessage, content: lastMessage.content + text }, - ]; - } else { - return [...prev, { role: "assistant", content: text }]; - } - }); - } catch (e) { - console.error(e); - } - } - }; - - // https://web.dev/streams/#the-getreader-and-read-methods - const reader = data.getReader(); - const decoder = new TextDecoder(); - const parser = createParser(onParse); - let done = false; - - while (!done) { - const { value, done: doneReading } = await reader.read(); - done = doneReading; - const chunkValue = decoder.decode(value); - parser.feed(chunkValue); - } - setLoading(false); - }; - - async function handleSourcesAndChat(question: string) { - setIsLoadingSources(true); - let sourcesResponse = await fetch("/api/getSources", { - method: "POST", - body: JSON.stringify({ question }), - }); - let sources; - if (sourcesResponse.ok) { - sources = await sourcesResponse.json(); - - setSources(sources); - } else { - setSources([]); - } - setIsLoadingSources(false); - - const parsedSourcesRes = await fetch("/api/getParsedSources", { - method: "POST", - body: JSON.stringify({ sources }), - }); - let parsedSources; - if (parsedSourcesRes.ok) { - parsedSources = await parsedSourcesRes.json(); - } - - const initialMessage = [ - { role: "system", content: getSystemPrompt(parsedSources, ageGroup) }, - { role: "user", content: `${question}` }, - ]; - setMessages(initialMessage); - await handleChat(initialMessage); - } - - return ( - <> -
- -
- {showResult ? ( -
-
-
- - -
-
-
- ) : ( - - )} -
- {/*