Skip to content

Commit

Permalink
display assistant suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
znamenskii-ilia committed Oct 9, 2024
1 parent 1c91a8a commit b96e900
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const _AIChat = (props: AIChatProps, ref: ForwardedRef<HTMLDivElement>) => {
// assistantName,
chatTitle,
isWaitingForResponse = false,
onApplyAssistantSuggestion,
onPromptChange,
onSubmit,
prompt,
Expand Down Expand Up @@ -56,7 +57,12 @@ const _AIChat = (props: AIChatProps, ref: ForwardedRef<HTMLDivElement>) => {

<ul className={styles.thread} data-testid="t--aichat-thread">
{thread.map((message: ChatMessage) => (
<ThreadMessage {...message} key={message.id} username={username} />
<ThreadMessage
{...message}
key={message.id}
onApplyAssistantSuggestion={onApplyAssistantSuggestion}
username={username}
/>
))}
</ul>

Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<HeadlessButton className={clsx(styles.root, className)} {...rest}>
{children}
</HeadlessButton>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./AssistantSuggestionButton";
export * from "./types";
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { PropsWithChildren } from "react";
import type { ButtonProps as HeadlessButtonProps } from "react-aria-components";

export interface AssistantSuggestionButtonProps
extends PropsWithChildren<HeadlessButtonProps> {}
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -12,6 +13,8 @@ export const ThreadMessage = ({
className,
content,
isAssistant,
onApplyAssistantSuggestion,
promptSuggestions = [],
username,
...rest
}: ThreadMessageProps) => {
Expand Down Expand Up @@ -50,6 +53,20 @@ export const ThreadMessage = ({
{content}
</Markdown>
</Text>

{promptSuggestions.length > 0 && (
<div className={styles.suggestions}>
{promptSuggestions.map((suggestion) => (
<AssistantSuggestionButton
key={suggestion}
// eslint-disable-next-line react-perf/jsx-no-new-function-as-prop
onPress={() => onApplyAssistantSuggestion?.(suggestion)}
>
{suggestion}
</AssistantSuggestionButton>
))}
</div>
)}
</div>
) : (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ export interface ThreadMessageProps extends HTMLProps<HTMLLIElement> {
content: string;
isAssistant: boolean;
username: string;
promptSuggestions?: string[];
onApplyAssistantSuggestion?: (suggestion: string) => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export interface ChatMessage {
id: string;
content: string;
isAssistant: boolean;
promptSuggestions?: string[];
}

export interface AIChatProps {
Expand All @@ -15,4 +16,5 @@ export interface AIChatProps {
isWaitingForResponse?: boolean;
onPromptChange: (prompt: string) => void;
onSubmit?: () => void;
onApplyAssistantSuggestion?: (suggestion: string) => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ import {

export interface WDSAIChatWidgetProps
extends ContainerWidgetProps<WidgetProps> {}

export interface Message {
id: string;
content: string;
role: "assistant" | "user" | "system";
promptSuggestions?: string[];
}

interface State extends WidgetState {
Expand All @@ -43,24 +45,7 @@ class WDSAIChatWidget extends BaseWidget<WDSAIChatWidgetProps, State> {
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,
};
Expand Down Expand Up @@ -123,12 +108,30 @@ class WDSAIChatWidget extends BaseWidget<WDSAIChatWidgetProps, State> {
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<HTMLFormElement>) => {
event?.preventDefault();
Expand Down Expand Up @@ -182,6 +185,8 @@ class WDSAIChatWidget extends BaseWidget<WDSAIChatWidgetProps, State> {
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,
Expand All @@ -190,7 +195,11 @@ class WDSAIChatWidget extends BaseWidget<WDSAIChatWidgetProps, State> {
};

handlePromptChange = (prompt: string) => {
this.setState({ prompt });
this.updatePrompt(prompt);
};

handleApplyAssistantSuggestion = (suggestion: string) => {
this.updatePrompt(suggestion);
};

getWidgetView(): ReactNode {
Expand All @@ -199,6 +208,7 @@ class WDSAIChatWidget extends BaseWidget<WDSAIChatWidgetProps, State> {
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}
Expand Down

0 comments on commit b96e900

Please sign in to comment.