diff --git a/app/api/getRelatedTopics/route.ts b/app/api/getRelatedTopics/route.ts new file mode 100644 index 0000000..fc6825c --- /dev/null +++ b/app/api/getRelatedTopics/route.ts @@ -0,0 +1,50 @@ +import { NextResponse } from "next/server"; + +export async function POST(request: Request) { + const { topic } = await request.json(); + try { + // Create a prompt for the model to generate related topics + const prompt = `Fill in this template with appropriate values: { "topic": "${topic}", "related_topics": [FILL_TOPIC1,FILL_TOPIC2, FILL_TOPIC3] }`; + const model = "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo"; + + // Make the call to generate topics + const res = await fetch("https://together.helicone.ai/v1/completions", { + headers: { + "Content-Type": "application/json", + "Helicone-Auth": `Bearer ${process.env.HELICONE_API_KEY}`, + Authorization: `Bearer ${process.env.TOGETHER_API_KEY ?? ""}`, + }, + method: "POST", + body: JSON.stringify({ prompt, model }), + }); + + const data = await res.json(); + const choices = data.choices; + let topics = []; + + // Check if there are choices and extract related topics + if (choices && choices.length > 0 && choices[0].text) { + const text = choices[0].text; + // Use a regular expression to find JSON-like content + const jsonMatch = text.match(/\{[^]*\}/); + + if (jsonMatch) { + try { + const jsonContent = JSON.parse(jsonMatch[0]); + // Ensure related_topics is an array before assigning + if (jsonContent.related_topics && Array.isArray(jsonContent.related_topics)) { + topics = jsonContent.related_topics.slice(0, 3); // Limit to a maximum of 3 related topics + } + } catch (parseError) { + console.error("Failed to parse JSON:", parseError); + } + } + } + return NextResponse.json({ topics }); + } catch (e) { + console.error(e); + return new Response("Error. Failed to generate related topics.", { + status: 500, + }); + } +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 32279ca..44e6830 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -7,8 +7,8 @@ import Sources from "@/components/Sources"; import { useState } from "react"; import { createParser, - ParsedEvent, - ReconnectInterval, + type ParsedEvent, + type ReconnectInterval, } from "eventsource-parser"; import { getSystemPrompt } from "@/utils/utils"; import Chat from "@/components/Chat"; @@ -25,13 +25,14 @@ export default function Home() { const [loading, setLoading] = useState(false); const [ageGroup, setAgeGroup] = useState("Middle School"); - const handleInitialChat = async () => { + const handleInitialChat = async (newTopic?: string) => { setShowResult(true); setLoading(true); - setTopic(inputValue); + setMessages([]); + setTopic(newTopic ?? inputValue); setInputValue(""); - await handleSourcesAndChat(inputValue); + await handleSourcesAndChat(newTopic ?? inputValue); setLoading(false); }; @@ -134,7 +135,9 @@ export default function Home() {
{showResult ? (
@@ -147,6 +150,7 @@ export default function Home() { setPromptValue={setInputValue} setMessages={setMessages} handleChat={handleChat} + handleInitialChat={handleInitialChat} topic={topic} /> diff --git a/components/Chat.tsx b/components/Chat.tsx index 6aa7bc3..1253c7e 100644 --- a/components/Chat.tsx +++ b/components/Chat.tsx @@ -3,6 +3,7 @@ import FinalInputArea from "./FinalInputArea"; import { useEffect, useRef, useState } from "react"; import simpleLogo from "../public/simple-logo.png"; import Image from "next/image"; +import RelatedTopics from "./RelatedTopics"; export default function Chat({ messages, @@ -12,6 +13,7 @@ export default function Chat({ setMessages, handleChat, topic, + handleInitialChat, }: { messages: { role: string; content: string }[]; disabled: boolean; @@ -22,6 +24,7 @@ export default function Chat({ >; handleChat: () => void; topic: string; + handleInitialChat: () => Promise; }) { const messagesEndRef = useRef(null); const scrollableContainerRef = useRef(null); @@ -60,7 +63,7 @@ export default function Chat({ return (
-
+

Topic: {topic} @@ -99,7 +102,9 @@ export default function Chat({ {Array.from(Array(10).keys()).map((i) => (

))} @@ -108,7 +113,13 @@ export default function Chat({
-
+ + +
>; + handleInitialChat: (newTopic?: string) => Promise; +}) { + const [relatedTopics, setRelatedTopics] = useState([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + // Function to fetch related topics from the API + const fetchRelatedTopics = async () => { + setLoading(true); + try { + const response = await fetch("/api/getRelatedTopics", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ topic }), + }); + + if (response.ok) { + const data = await response.json(); + setRelatedTopics(data.topics); + } + } catch (error) { + console.error("Failed to fetch related topics:", error); + } finally { + setLoading(false); + } + }; + + if (topic) { + fetchRelatedTopics(); // Fetch topics if a topic is provided + } + }, [topic]); + + // Function to handle click on a related topic + const handleTopicClick = (relatedTopic: string) => { + setPromptValue(relatedTopic); // Set the prompt value to the clicked topic + handleInitialChat(relatedTopic); // Initiate chat with the clicked topic + }; + + return ( +
+

+ Related Topics: +

+ {loading ? ( +

Loading...

+ ) : relatedTopics.length > 0 ? ( +
    + {relatedTopics.map((relatedTopic, index) => ( + + {index > 0 && "|"} +
  • handleTopicClick(relatedTopic)} + className="text-blue-500 hover:cursor-pointer" + > + {relatedTopic} +
  • +
    + ))} +
+ ) : null} +
+ ); +}