From f3d5055e1d34a26ba81f6e8e6858054deade0d39 Mon Sep 17 00:00:00 2001 From: Jon Kafton <939376+jonkafton@users.noreply.github.com> Date: Wed, 5 Feb 2025 22:12:20 +0100 Subject: [PATCH 01/18] Update chat page to use Smoot Design AiChat --- frontends/main/package.json | 2 +- .../main/src/app-pages/ChatPage/ChatPage.tsx | 61 +++----------- frontends/main/src/app-pages/ChatPage/send.ts | 78 ------------------ .../AiRecommendationBot.tsx | 79 +++++++++++++++++++ yarn.lock | 25 +++++- 5 files changed, 115 insertions(+), 130 deletions(-) delete mode 100644 frontends/main/src/app-pages/ChatPage/send.ts create mode 100644 frontends/main/src/page-components/AiRecommendationBot/AiRecommendationBot.tsx diff --git a/frontends/main/package.json b/frontends/main/package.json index 2b3985f5ce..171a5236d3 100644 --- a/frontends/main/package.json +++ b/frontends/main/package.json @@ -14,7 +14,7 @@ "@emotion/cache": "^11.13.1", "@emotion/styled": "^11.11.0", "@mitodl/course-search-utils": "3.3.2", - "@mitodl/smoot-design": "^3.0.1", + "@mitodl/smoot-design": "^3.1.0", "@next/bundle-analyzer": "^14.2.15", "@nlux/react": "^2.17.1", "@nlux/themes": "^2.17.1", diff --git a/frontends/main/src/app-pages/ChatPage/ChatPage.tsx b/frontends/main/src/app-pages/ChatPage/ChatPage.tsx index 056719b8ab..62349c8b00 100644 --- a/frontends/main/src/app-pages/ChatPage/ChatPage.tsx +++ b/frontends/main/src/app-pages/ChatPage/ChatPage.tsx @@ -1,61 +1,22 @@ "use client" -import React, { useMemo } from "react" -import { makeSend } from "./send" -import { FeatureFlags } from "@/common/feature_flags" -import { useFeatureFlagEnabled } from "posthog-js/react" -import StyledContainer from "@/page-components/StyledContainer/StyledContainer" +import React from "react" import { styled } from "ol-components" -import { NluxAiChat } from "@/page-components/Nlux-AiChat/AiChat" +import AiRecommendationBot from "@/page-components/AiRecommendationBot/AiRecommendationBot" -const StyledChat = styled(NluxAiChat)({ - maxHeight: "60vh", - flex: 1, +const Container = styled.div({ + height: "100%", + padding: "24px 0", + display: "flex", + alignItems: "center", + justifyContent: "center", }) -const CONVERSATION_OPTIONS = { - conversationStarters: [ - { - prompt: - "I'm interested in courses on quantum computing that offer certificates.", - }, - { - prompt: - "I want to learn about global warming, can you recommend any videos?", - }, - { - prompt: - "I am curious about AI applications for business. Do you have any free courses about that?", - }, - { - prompt: - "I would like to learn about linear regression, preferably at no cost.", - }, - ], -} - const ChatPage = () => { - const recommendationBotEnabled = useFeatureFlagEnabled( - FeatureFlags.RecommendationBot, - ) - const send = useMemo(() => { - return makeSend({ url: "/api/v0/chat_agent/" }) - }, []) return ( - - { - // eslint-disable-next-line no-constant-condition - recommendationBotEnabled ? ( - - ) : ( - <> - ) - } - + + + ) } diff --git a/frontends/main/src/app-pages/ChatPage/send.ts b/frontends/main/src/app-pages/ChatPage/send.ts deleted file mode 100644 index edcc672760..0000000000 --- a/frontends/main/src/app-pages/ChatPage/send.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { NluxAiChatProps } from "@/page-components/Nlux-AiChat/AiChat" - -function getCookie(name: string) { - const value = `; ${document.cookie}` - const parts = value.split(`; ${name}=`) - if (parts.length === 2) { - return parts.pop()?.split(";").shift() - } -} - -type EndpointOpts = { - url: "/api/v0/chat_agent/" - extraBody?: Record -} - -const makeRequest = async (opts: EndpointOpts, message: string) => - fetch(`${process.env.NEXT_PUBLIC_MITOL_API_BASE_URL}${opts.url}`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-CSRFToken": - getCookie(process.env.NEXT_PUBLIC_CSRF_COOKIE_NAME || "csrftoken") ?? - "", - }, - credentials: "include", // TODO Remove this, should be handled by same-origin - body: JSON.stringify({ message, ...opts.extraBody }), - }) - -const RESPONSE_DELAY = 500 - -// Function to send query to the server and receive a stream of chunks as response -const makeSend = - ( - opts: EndpointOpts, - processContent: (content: string) => string = (content) => content, - ): NluxAiChatProps["send"] => - async (message, observer) => { - const response = await makeRequest(opts, message) - - if (response.status !== 200) { - observer.error(new Error("Failed to connect to the server")) - return - } - - if (!response.body) { - return - } - - // Read a stream of server-sent events - // and feed them to the observer as they are being generated - const reader = response.body.getReader() - const textDecoder = new TextDecoder() - - // eslint-disable-next-line no-constant-condition - while (true) { - const { value, done } = await reader.read() - if (done) { - /** - * Without the pause here, some messages were getting displayed completely - * empty. Unsure why. - * - * Maybe related to stream having only a single chunk? - */ - await new Promise((res) => setTimeout(res, RESPONSE_DELAY)) - break - } - - const content = textDecoder.decode(value) - if (content) { - observer.next(processContent(content)) - } - } - - observer.complete() - } - -export { makeSend } -export type { EndpointOpts } diff --git a/frontends/main/src/page-components/AiRecommendationBot/AiRecommendationBot.tsx b/frontends/main/src/page-components/AiRecommendationBot/AiRecommendationBot.tsx new file mode 100644 index 0000000000..8850516769 --- /dev/null +++ b/frontends/main/src/page-components/AiRecommendationBot/AiRecommendationBot.tsx @@ -0,0 +1,79 @@ +import React from "react" +import { styled } from "ol-components" +import { getCsrfToken } from "@/common/utils" + +import { AiChat, AiChatProps } from "@mitodl/smoot-design/ai" + +const Container = styled.div(({ theme }) => ({ + width: "800px", + height: "100%", + [theme.breakpoints.down("md")]: { + width: "100%", + }, +})) + +const AiChatStyled = styled(AiChat)({ + height: "60vh", +}) + +const INITIAL_MESSAGES: AiChatProps["initialMessages"] = [ + { + content: "What do you want to learn about today?", + role: "assistant", + }, + { + content: + "I'm interested in courses on quantum computing that offer certificates.", + role: "user", + }, +] + +export const STARTERS = [ + { + content: + "I'm interested in courses on quantum computing that offer certificates.", + }, + { + content: + "I want to learn about global warming, can you recommend any videos?", + }, + { + content: + "I am curious about AI applications for business. Do you have any free courses about that?", + }, + { + content: + "I would like to learn about linear regression, preferably at no cost.", + }, +] + +const AiRecommendationBot = () => { + return ( + + ({ + message: messages[messages.length - 1].content, + }), + // onFinish: (message) => { + // const contentParts = message.content.split("