From b96e900e75fdb948f338bacfe131004e97df152b Mon Sep 17 00:00:00 2001 From: Ilia Znamenskii Date: Wed, 9 Oct 2024 12:41:15 +0200 Subject: [PATCH] display assistant suggestions --- .../src/components/AIChat/src/AIChat.tsx | 8 ++- .../AssistantSuggestionButton.tsx | 17 ++++++ .../src/AssistantSuggestionButton/index.ts | 2 + .../styles.module.css | 23 +++++++ .../src/AssistantSuggestionButton/types.ts | 5 ++ .../src/ThreadMessage/ThreadMessage.tsx | 17 ++++++ .../src/ThreadMessage/styles.module.css | 7 +++ .../AIChat/src/ThreadMessage/types.ts | 2 + .../src/components/AIChat/src/types.ts | 2 + .../ui/wds/WDSAIChatWidget/widget/index.tsx | 60 +++++++++++-------- 10 files changed, 117 insertions(+), 26 deletions(-) create mode 100644 app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/AssistantSuggestionButton.tsx create mode 100644 app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/index.ts create mode 100644 app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/styles.module.css create mode 100644 app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/types.ts diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/AIChat.tsx b/app/client/packages/design-system/widgets/src/components/AIChat/src/AIChat.tsx index d6975d9750a..40a50d43754 100644 --- a/app/client/packages/design-system/widgets/src/components/AIChat/src/AIChat.tsx +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/AIChat.tsx @@ -14,6 +14,7 @@ const _AIChat = (props: AIChatProps, ref: ForwardedRef) => { // assistantName, chatTitle, isWaitingForResponse = false, + onApplyAssistantSuggestion, onPromptChange, onSubmit, prompt, @@ -56,7 +57,12 @@ const _AIChat = (props: AIChatProps, ref: ForwardedRef) => {
    {thread.map((message: ChatMessage) => ( - + ))}
diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/AssistantSuggestionButton.tsx b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/AssistantSuggestionButton.tsx new file mode 100644 index 00000000000..6256a1d9ab5 --- /dev/null +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/AssistantSuggestionButton.tsx @@ -0,0 +1,17 @@ +import { clsx } from "clsx"; +import React from "react"; +import { Button as HeadlessButton } from "react-aria-components"; +import styles from "./styles.module.css"; +import type { AssistantSuggestionButtonProps } from "./types"; + +export const AssistantSuggestionButton = ({ + children, + className, + ...rest +}: AssistantSuggestionButtonProps) => { + return ( + + {children} + + ); +}; diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/index.ts b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/index.ts new file mode 100644 index 00000000000..c9075961d61 --- /dev/null +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/index.ts @@ -0,0 +1,2 @@ +export * from "./AssistantSuggestionButton"; +export * from "./types"; diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/styles.module.css b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/styles.module.css new file mode 100644 index 00000000000..eb7f6bc475a --- /dev/null +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/styles.module.css @@ -0,0 +1,23 @@ +.root { + height: 30px; + padding: 0 var(--inner-spacing-4); + color: var(--fg-neutral, #1f2123); + font-size: var(--type-body-text, 15px); + line-height: var(--type-body-text-lineheight, 21.13px); /* 140.867% */ + background-color: var(--bg-neutral-subtle-alt, #e7e8e8); + border-radius: var(--radius-inner-button, 1.8px); + + &:hover { + background-color: var(--bg-neutral-subtle-alt-hover, #f0f1f1); + } + + &:focus-visible { + box-shadow: + 0 0 0 2px var(--color-bg), + 0 0 0 4px var(--color-bd-focus); + } + + &:active { + background-color: var(--bg-neutral-subtle-alt-active, #e1e2e2); + } +} diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/types.ts b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/types.ts new file mode 100644 index 00000000000..20186f75d24 --- /dev/null +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/types.ts @@ -0,0 +1,5 @@ +import type { PropsWithChildren } from "react"; +import type { ButtonProps as HeadlessButtonProps } from "react-aria-components"; + +export interface AssistantSuggestionButtonProps + extends PropsWithChildren {} diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/ThreadMessage.tsx b/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/ThreadMessage.tsx index 37ea1f1c792..a80f3bd40e8 100644 --- a/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/ThreadMessage.tsx +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/ThreadMessage.tsx @@ -4,6 +4,7 @@ import React from "react"; import Markdown from "react-markdown"; import SyntaxHighlighter from "react-syntax-highlighter"; import { monokai } from "react-syntax-highlighter/dist/cjs/styles/hljs"; +import { AssistantSuggestionButton } from "../AssistantSuggestionButton"; import { UserAvatar } from "../UserAvatar"; import styles from "./styles.module.css"; import type { ThreadMessageProps } from "./types"; @@ -12,6 +13,8 @@ export const ThreadMessage = ({ className, content, isAssistant, + onApplyAssistantSuggestion, + promptSuggestions = [], username, ...rest }: ThreadMessageProps) => { @@ -50,6 +53,20 @@ export const ThreadMessage = ({ {content} + + {promptSuggestions.length > 0 && ( +
+ {promptSuggestions.map((suggestion) => ( + onApplyAssistantSuggestion?.(suggestion)} + > + {suggestion} + + ))} +
+ )} ) : ( <> diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/styles.module.css b/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/styles.module.css index e8081ecad00..7ac0684b115 100644 --- a/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/styles.module.css +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/styles.module.css @@ -25,3 +25,10 @@ /* TODO: --type-caption-lineheight doesn't exists. Define it */ line-height: var(--type-caption-lineheight, 17.25px); } + +.suggestions { + display: flex; + gap: var(--inner-spacing-5); + flex-wrap: wrap; + padding: var(--inner-spacing-4) 0 0; +} diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/types.ts b/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/types.ts index 8935dfe1e5c..4459c37a5a9 100644 --- a/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/types.ts +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/types.ts @@ -4,4 +4,6 @@ export interface ThreadMessageProps extends HTMLProps { content: string; isAssistant: boolean; username: string; + promptSuggestions?: string[]; + onApplyAssistantSuggestion?: (suggestion: string) => void; } diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/types.ts b/app/client/packages/design-system/widgets/src/components/AIChat/src/types.ts index 69572c5d6f8..e27a804b91c 100644 --- a/app/client/packages/design-system/widgets/src/components/AIChat/src/types.ts +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/types.ts @@ -2,6 +2,7 @@ export interface ChatMessage { id: string; content: string; isAssistant: boolean; + promptSuggestions?: string[]; } export interface AIChatProps { @@ -15,4 +16,5 @@ export interface AIChatProps { isWaitingForResponse?: boolean; onPromptChange: (prompt: string) => void; onSubmit?: () => void; + onApplyAssistantSuggestion?: (suggestion: string) => void; } diff --git a/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/index.tsx b/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/index.tsx index f94fb5935f7..ef7568ca766 100644 --- a/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/index.tsx +++ b/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/index.tsx @@ -27,10 +27,12 @@ import { export interface WDSAIChatWidgetProps extends ContainerWidgetProps {} + export interface Message { id: string; content: string; role: "assistant" | "user" | "system"; + promptSuggestions?: string[]; } interface State extends WidgetState { @@ -43,24 +45,7 @@ class WDSAIChatWidget extends BaseWidget { static type = "WDS_AI_CHAT_WIDGET"; state = { - messages: [ - { - id: "1", - content: "Hello! How can I help you?", - role: "assistant" as const, - }, - { - id: "2", - content: "Find stuck support requests", - role: "user" as const, - }, - { - id: "3", - content: - "I'm finding these customer support requests that have been waiting for a response for over a day:", - role: "assistant" as const, - }, - ], + messages: [], prompt: "", isWaitingForResponse: false, }; @@ -123,12 +108,30 @@ class WDSAIChatWidget extends BaseWidget { return {}; } - adaptMessages(messages: Message[]): ChatMessage[] { - return messages.map((message) => ({ - ...message, - isAssistant: message.role === "assistant", - })); - } + updatePrompt = (prompt: string) => { + this.setState({ prompt }); + }; + + adaptMessages = (messages: Message[]): ChatMessage[] => { + const chatMessages: ChatMessage[] = messages.map((message) => { + if (message.role === "assistant") { + return { + id: message.id, + content: message.content, + isAssistant: true, + promptSuggestions: message.promptSuggestions || [], + }; + } + + return { + id: message.id, + content: message.content, + isAssistant: false, + }; + }); + + return chatMessages; + }; handleMessageSubmit = (event?: FormEvent) => { event?.preventDefault(); @@ -182,6 +185,8 @@ class WDSAIChatWidget extends BaseWidget { id: Math.random().toString(), content: this.props.queryData.choices[0].message.content, role: "assistant", + // TODO: Add prompt suggestions from the query data, if any + promptSuggestions: [], }, ], isWaitingForResponse: false, @@ -190,7 +195,11 @@ class WDSAIChatWidget extends BaseWidget { }; handlePromptChange = (prompt: string) => { - this.setState({ prompt }); + this.updatePrompt(prompt); + }; + + handleApplyAssistantSuggestion = (suggestion: string) => { + this.updatePrompt(suggestion); }; getWidgetView(): ReactNode { @@ -199,6 +208,7 @@ class WDSAIChatWidget extends BaseWidget { assistantName={this.props.assistantName} chatTitle={this.props.chatTitle} isWaitingForResponse={this.state.isWaitingForResponse} + onApplyAssistantSuggestion={this.handleApplyAssistantSuggestion} onPromptChange={this.handlePromptChange} onSubmit={this.handleMessageSubmit} prompt={this.state.prompt}