Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DOCSP-32954] Add Rating, Character Count, Initial, Disclaimer #176

Merged
merged 10 commits into from
Sep 22, 2023
9 changes: 5 additions & 4 deletions chat-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,15 @@
"@leafygreen-ui/text-input": "^12.1.18",
"@leafygreen-ui/toggle": "^10.0.15",
"@leafygreen-ui/typography": "^16.5.4",
"@lg-chat/chat-disclaimer": "^1.0.1",
"@lg-chat/fixed-chat-window": "^1.1.1",
"@lg-chat/message-prompts": "^1.0.1",
"@lg-chat/avatar": "^2.0.6",
"@lg-chat/chat-window": "^1.0.4",
"@lg-chat/input-bar": "^3.1.3",
"@lg-chat/message": "^2.0.8",
"@lg-chat/message-feed": "^2.0.7",
"@lg-chat/chat-disclaimer": "^2.0.0",
"@lg-chat/chat-window": "^1.0.4",
"@lg-chat/fixed-chat-window": "^1.1.1",
"@lg-chat/leafygreen-chat-provider": "^1.0.2",
"@lg-chat/message-prompts": "^1.0.2",
"@lg-chat/message-rating": "^1.1.3",
"@microsoft/fetch-event-source": "^2.0.1",
"buffer": "^6.0.3",
Expand Down
3 changes: 0 additions & 3 deletions chat-ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ import { Overline, Link } from "@leafygreen-ui/typography";
import Toggle from "@leafygreen-ui/toggle";
import { Chatbot as DevCenterChatbot } from "./DevCenterChatbot";

const prefersDarkMode = () =>
window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;

const prefersDarkMode = () =>
window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;

Expand Down
11 changes: 2 additions & 9 deletions chat-ui/src/Chatbot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,13 @@ const styles = {
box-sizing: border-box;
}
}`,
chatbot_input_area: css`
chatbot_input: css`
position: relative;
width: 100%;
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-top: 1rem;
padding-left: 32px;
padding-right: 32px;
padding-top: 0.5rem;
padding-bottom: 1rem;
`,
chatbot_input_error_border: css`
> div {
Expand Down Expand Up @@ -535,10 +531,7 @@ function ChatbotModal({
})}
</MessageFeed>
) : null}
<div className={cx(
styles.chatbot_input,
styles.chatbot_input_area
)}>
<div className={cx(styles.chatbot_input, styles.chatbot_input_area)}>
{conversation.error ? (
<ErrorBanner darkMode={darkMode} message={conversation.error} />
) : null}
Expand Down
177 changes: 145 additions & 32 deletions chat-ui/src/DevCenterChatbot.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import { css } from "@emotion/css";
import LeafyGreenProvider from "@leafygreen-ui/leafygreen-provider";
import LeafyGreenProvider, {
useDarkMode,
} from "@leafygreen-ui/leafygreen-provider";
import Modal from "@leafygreen-ui/modal";
import { palette } from "@leafygreen-ui/palette";
import { ParagraphSkeleton } from "@leafygreen-ui/skeleton-loader";
import { Body, Link } from "@leafygreen-ui/typography";
import { Avatar } from "@lg-chat/avatar";
import { DisclaimerText as LGDisclaimerText } from "@lg-chat/chat-disclaimer";
import { ChatWindow } from "@lg-chat/chat-window";
import { ChatTrigger } from "@lg-chat/fixed-chat-window";
import { InputBar } from "@lg-chat/input-bar";
import {
InputBar as LGInputBar,
InputBarProps as LGInputBarProps,
} from "@lg-chat/input-bar";
import { LeafyGreenChatProvider } from "@lg-chat/leafygreen-chat-provider";
import { Message as LGMessage, MessageSourceType } from "@lg-chat/message";
import { MessageFeed } from "@lg-chat/message-feed";
import { MessagePrompt, MessagePrompts } from "@lg-chat/message-prompts";
import { MessageRatingProps } from "@lg-chat/message-rating";
import { Fragment, useEffect, useState } from "react";
import { CharacterCount } from "./InputBar";
import { UserProvider } from "./UserProvider";
import { MessageData } from "./services/conversations";
import { Conversation, useConversation } from "./useConversation";
// import { DisclaimerText } from "@lg-chat/chat-disclaimer";
import { User, useUser } from "./useUser";

const styles = {
chat_trigger: css`
Expand Down Expand Up @@ -60,13 +70,10 @@ const styles = {
}
}
`,
disclaimer_text_container: css`
display: flex;
`,
disclaimer_text: css`
& p {
text-align: center;
}
text-align: center;
margin-top: 16px;
margin-bottom: 32px;
`,
chatbot_input: css`
padding-bottom: 1rem;
Expand Down Expand Up @@ -104,6 +111,26 @@ const styles = {
transition: 'opacity 300ms ease-in',
opacity: 1,
`,
chatbot_input_area: css`
position: relative;
width: 100%;
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-top: 1rem;
padding-left: 32px;
padding-right: 32px;
padding-top: 0.5rem;
padding-bottom: 1rem;
`,
chatbot_input_error_border: css`
> div {
> div {
border-color: ${palette.red.base} !important;
border-width: 2px !important;
}
}
`,
};

export type ChatbotProps = {
Expand All @@ -112,14 +139,17 @@ export type ChatbotProps = {
shouldStream?: boolean;
suggestedPrompts?: string[];
startingMessage?: string;
user?: User;
};

export function Chatbot(props: ChatbotProps) {
const { darkMode, ...InnerChatbotProps } = props;
const { darkMode, user, ...InnerChatbotProps } = props;
// TODO: Use ConversationProvider
return (
<LeafyGreenProvider darkMode={props.darkMode}>
<InnerChatbot {...InnerChatbotProps} />
<UserProvider user={user}>
<InnerChatbot {...InnerChatbotProps} />
</UserProvider>
</LeafyGreenProvider>
);
}
Expand Down Expand Up @@ -203,17 +233,6 @@ export function InnerChatbot({
);
}

// const DisclaimerTextDescription = () => {
// return (
// <div className={styles.disclaimer_text}>
// This is a
// <Link href={"https://www.mongodb.com/legal/terms-of-use"}>
// Terms of Use
// </Link>
// </div>
// );
// };

const SUGGESTED_PROMPTS = [
"How do you deploy a free cluster in Atlas?",
"How do you import or migrate data into MongoDB Atlas?",
Expand Down Expand Up @@ -279,6 +298,10 @@ function ChatbotModal({
);
};

const promptIsTooLong = () => {
return inputBarValue.length > MAX_INPUT_CHARACTERS;
};

return (
<Modal
open={open}
Expand All @@ -293,12 +316,7 @@ function ChatbotModal({
className={styles.chat_window}
>
<MessageFeed>
{/* <DisclaimerText
className={styles.disclaimer_text_container}
title="Terms of Use"
>
{DisclaimerTextDescription()}
</DisclaimerText> */}
<DisclaimerText />
{messages.map((messageData, idx) => {
return (
<Message
Expand All @@ -310,14 +328,18 @@ function ChatbotModal({
}
suggestedPromptOnClick={handleSubmit}
isLoading={isLoading(messageData.id)}
renderRating={messageData.role === "assistant" && idx !== 0}
conversation={conversation}
/>
);
})}
</MessageFeed>
<InputBar
inputBarValue={inputBarValue}
onSubmit={() => handleSubmit(inputBarValue)}
hasError={promptIsTooLong()}
disabled={!!conversation.error}
disableSend={awaitingReply}
disableSend={awaitingReply || promptIsTooLong()}
textareaProps={{
value: inputBarValue,
onChange: (e) => {
Expand All @@ -336,12 +358,80 @@ function ChatbotModal({
);
}

const MAX_INPUT_CHARACTERS = 300;
interface InputBarProps extends LGInputBarProps {
inputBarValue: string;
hasError: boolean;
}

const InputBar = (props: InputBarProps) => {
const { inputBarValue, hasError, ...LGInputBarProps } = props;
const { darkMode } = useDarkMode();

return (
<div className={styles.chatbot_input_area}>
<LGInputBar
className={
hasError ?? false ? styles.chatbot_input_error_border : undefined
}
shouldRenderGradient={!hasError}
{...LGInputBarProps}
/>
<div
className={css`
display: flex;
justify-content: space-between;
`}
>
<Body baseFontSize={13}>
This is an experimental generative AI chatbot. All information should
be verified prior to use.
</Body>
<CharacterCount
darkMode={darkMode}
current={inputBarValue.length}
max={MAX_INPUT_CHARACTERS}
/>
</div>
</div>
);
};

const DisclaimerText = () => {
return (
<LGDisclaimerText
title="Terms and Policy"
className={styles.disclaimer_text}
>
<Body>
This is a generative AI Chatbot. By interacting with it, you agree to
MongoDB's{" "}
<Link
hideExternalIcon
href={"https://www.mongodb.com/legal/terms-of-use"}
>
Terms of Use
</Link>{" "}
and{" "}
<Link
hideExternalIcon
href={"https://www.mongodb.com/legal/acceptable-use-policy"}
>
Acceptable Use Policy.
</Link>
</Body>
</LGDisclaimerText>
);
};

type MessageProp = {
messageData: MessageData;
suggestedPrompts?: string[];
displaySuggestedPrompts: boolean;
suggestedPromptOnClick: (prompt: string) => void;
isLoading: boolean;
renderRating: boolean;
conversation: Conversation;
};

const Message = ({
Expand All @@ -350,25 +440,48 @@ const Message = ({
displaySuggestedPrompts,
suggestedPromptOnClick,
isLoading,
renderRating,
conversation,
}: MessageProp) => {
const [suggestedPromptIdx, setSuggestedPromptIdx] = useState(-1);
const user = useUser();

return (
<Fragment key={messageData.id}>
<LGMessage
baseFontSize={13}
isSender={messageData.role === "user"}
messageRatingProps={
messageData.role === "assistant"
renderRating
? {
className: styles.message_rating,
description: "How was the response?",
onChange: (e) => console.log(e),
onChange: (e) => {
const value = e.target.value as MessageRatingProps["value"];
if (!value) {
return;
}
conversation.rateMessage(
messageData.id,
value === "liked" ? true : false
);
},
value:
messageData.rating === undefined
? undefined
: messageData.rating
? "liked"
: "disliked",
}
: undefined
}
avatar={
<Avatar variant={messageData.role === "user" ? "user" : "mongo"} />
<Avatar
variant={messageData.role === "user" ? "user" : "mongo"}
name={
messageData.role === "user" && user?.name ? user?.name : undefined
}
/>
}
sourceType={isLoading ? undefined : MessageSourceType.Markdown}
markdownProps={{
Expand Down
13 changes: 13 additions & 0 deletions chat-ui/src/UserProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createContext, ReactNode } from "react";
import { User } from "./useUser";

export const UserContext = createContext<User | undefined>(undefined);

type UserProviderProps = {
children: ReactNode;
user?: User;
};

export function UserProvider({ children, user }: UserProviderProps) {
return <UserContext.Provider value={user}>{children}</UserContext.Provider>;
}
10 changes: 10 additions & 0 deletions chat-ui/src/useUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useContext } from "react";
import { UserContext } from "./UserProvider";

export type User = {
name: string;
};

export function useUser() {
return useContext(UserContext);
}
Loading