feat: add initial assistant message (#36798)
## Description  Fixes #36776 > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.Sanity" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/11275055683> > Commit: a8f155422725c5310b7ac37d49a57995ee20f732 > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11275055683&attempt=2" target="_blank">Cypress dashboard</a>. > Tags: `@tag.Sanity` > Spec: > <hr>Thu, 10 Oct 2024 14:29:09 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Introduced `AssistantSuggestionButton` for enhanced user interaction in the AI chat. - Added support for displaying and applying assistant suggestions in chat threads. - Implemented an editable array component for managing string pairs. - Enhanced configuration options with new properties for initial assistant messages and suggestions. - **Improvements** - Improved state management for dynamic messages in the AI chat widget. - Updated rendering logic for conditional display of suggestions in chat messages. - Added new props to facilitate better interaction and suggestion handling in chat components. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
5fadce541d
commit
a0814e1438
|
|
@ -14,6 +14,7 @@ const _AIChat = (props: AIChatProps, ref: ForwardedRef<HTMLDivElement>) => {
|
|||
// assistantName,
|
||||
chatTitle,
|
||||
isWaitingForResponse = false,
|
||||
onApplyAssistantSuggestion,
|
||||
onPromptChange,
|
||||
onSubmit,
|
||||
prompt,
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
import { Text } from "@appsmith/wds";
|
||||
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}>
|
||||
<Text>{children}</Text>
|
||||
</HeadlessButton>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export * from "./AssistantSuggestionButton";
|
||||
export * from "./types";
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
.root {
|
||||
height: 30px;
|
||||
padding: 0 var(--inner-spacing-4);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import type { PropsWithChildren } from "react";
|
||||
import type { ButtonProps as HeadlessButtonProps } from "react-aria-components";
|
||||
|
||||
export interface AssistantSuggestionButtonProps
|
||||
extends PropsWithChildren<HeadlessButtonProps> {}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
import { Text } from "@appsmith/wds";
|
||||
import { Flex, Text } from "@appsmith/wds";
|
||||
import { clsx } from "clsx";
|
||||
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,25 @@ export const ThreadMessage = ({
|
|||
{content}
|
||||
</Markdown>
|
||||
</Text>
|
||||
|
||||
{promptSuggestions.length > 0 && (
|
||||
<Flex
|
||||
className={styles.suggestions}
|
||||
gap="var(--inner-spacing-5)"
|
||||
paddingTop="spacing-4"
|
||||
wrap="wrap"
|
||||
>
|
||||
{promptSuggestions.map((suggestion) => (
|
||||
<AssistantSuggestionButton
|
||||
key={suggestion}
|
||||
// eslint-disable-next-line react-perf/jsx-no-new-function-as-prop
|
||||
onPress={() => onApplyAssistantSuggestion?.(suggestion)}
|
||||
>
|
||||
{suggestion}
|
||||
</AssistantSuggestionButton>
|
||||
))}
|
||||
</Flex>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -4,4 +4,6 @@ export interface ThreadMessageProps extends HTMLProps<HTMLLIElement> {
|
|||
content: string;
|
||||
isAssistant: boolean;
|
||||
username: string;
|
||||
promptSuggestions?: string[];
|
||||
onApplyAssistantSuggestion?: (suggestion: string) => void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import {
|
|||
ATTR_SERVICE_INSTANCE_ID,
|
||||
} from "@opentelemetry/semantic-conventions/incubating";
|
||||
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
||||
import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";
|
||||
import { registerInstrumentations } from "@opentelemetry/instrumentation";
|
||||
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
|
||||
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
||||
|
|
|
|||
152
app/client/src/components/propertyControls/ArrayComponent.tsx
Normal file
152
app/client/src/components/propertyControls/ArrayComponent.tsx
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
import { Button } from "@appsmith/ads";
|
||||
import { debounce } from "lodash";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { ControlWrapper, InputGroup } from "./StyledControls";
|
||||
|
||||
function updateOptionLabel<T>(
|
||||
items: Array<T>,
|
||||
index: number,
|
||||
updatedLabel: string,
|
||||
) {
|
||||
return items.map((option: T, optionIndex) => {
|
||||
if (index !== optionIndex) {
|
||||
return option;
|
||||
}
|
||||
|
||||
return updatedLabel;
|
||||
});
|
||||
}
|
||||
|
||||
const StyledBox = styled.div`
|
||||
width: 10px;
|
||||
`;
|
||||
|
||||
type UpdateItemsFunction = (
|
||||
items: string[],
|
||||
isUpdatedViaKeyboard?: boolean,
|
||||
) => void;
|
||||
|
||||
interface ArrayComponentProps {
|
||||
items: string[];
|
||||
updateItems: UpdateItemsFunction;
|
||||
addLabel?: string;
|
||||
}
|
||||
|
||||
const StyledInputGroup = styled(InputGroup)`
|
||||
> .ads-v2-input__input-section > div {
|
||||
flex: 1;
|
||||
min-width: 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
export function ArrayComponent(props: ArrayComponentProps) {
|
||||
const [renderItems, setRenderItems] = useState<string[]>([]);
|
||||
const [typing, setTyping] = useState<boolean>(false);
|
||||
const { items } = props;
|
||||
|
||||
useEffect(() => {
|
||||
let { items } = props;
|
||||
|
||||
items = Array.isArray(items) ? items.slice() : [];
|
||||
|
||||
items.length !== 0 && !typing && setRenderItems(items);
|
||||
}, [props, items.length, renderItems.length, typing]);
|
||||
|
||||
const debouncedUpdateItems = useCallback(
|
||||
debounce((updatedItems: string[]) => {
|
||||
props.updateItems(updatedItems, true);
|
||||
}, 200),
|
||||
[props.updateItems],
|
||||
);
|
||||
|
||||
function updateKey(index: number, updatedKey: string) {
|
||||
let { items } = props;
|
||||
|
||||
items = Array.isArray(items) ? items : [];
|
||||
const updatedItems = updateOptionLabel(items, index, updatedKey);
|
||||
const updatedRenderItems = updateOptionLabel(
|
||||
renderItems,
|
||||
index,
|
||||
updatedKey,
|
||||
);
|
||||
|
||||
setRenderItems(updatedRenderItems);
|
||||
debouncedUpdateItems(updatedItems);
|
||||
}
|
||||
|
||||
function deleteItem(index: number, isUpdatedViaKeyboard = false) {
|
||||
let { items } = props;
|
||||
|
||||
items = Array.isArray(items) ? items : [];
|
||||
|
||||
const newItems = items.filter((o, i) => i !== index);
|
||||
const newRenderItems = renderItems.filter((o, i) => i !== index);
|
||||
|
||||
setRenderItems(newRenderItems);
|
||||
props.updateItems(newItems, isUpdatedViaKeyboard);
|
||||
}
|
||||
|
||||
function addItem(e: React.MouseEvent) {
|
||||
let { items } = props;
|
||||
|
||||
items = Array.isArray(items) ? items.slice() : [];
|
||||
|
||||
items.push("");
|
||||
|
||||
const updatedRenderItems = renderItems.slice();
|
||||
|
||||
updatedRenderItems.push("");
|
||||
|
||||
setRenderItems(updatedRenderItems);
|
||||
props.updateItems(items, e.detail === 0);
|
||||
}
|
||||
|
||||
function onInputFocus() {
|
||||
setTyping(true);
|
||||
}
|
||||
|
||||
function onInputBlur() {
|
||||
setTyping(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{renderItems.map((item: string, index) => {
|
||||
return (
|
||||
<ControlWrapper key={index} orientation={"HORIZONTAL"}>
|
||||
<StyledInputGroup
|
||||
dataType={"text"}
|
||||
onBlur={onInputBlur}
|
||||
onChange={(value: string) => updateKey(index, value)}
|
||||
onFocus={onInputFocus}
|
||||
value={item}
|
||||
/>
|
||||
<StyledBox />
|
||||
<Button
|
||||
isIconButton
|
||||
kind="tertiary"
|
||||
onClick={(e: React.MouseEvent) =>
|
||||
deleteItem(index, e.detail === 0)
|
||||
}
|
||||
size="sm"
|
||||
startIcon="delete-bin-line"
|
||||
/>
|
||||
</ControlWrapper>
|
||||
);
|
||||
})}
|
||||
|
||||
<div className="flex flex-row-reverse mt-1">
|
||||
<Button
|
||||
className="t--property-control-options-add"
|
||||
kind="tertiary"
|
||||
onClick={addItem}
|
||||
size="sm"
|
||||
startIcon="plus"
|
||||
>
|
||||
{props.addLabel || "Add suggestion"}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
48
app/client/src/components/propertyControls/ArrayControl.tsx
Normal file
48
app/client/src/components/propertyControls/ArrayControl.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import { objectKeys } from "@appsmith/utils";
|
||||
import type { DropdownOption } from "components/constants";
|
||||
import React from "react";
|
||||
import { isDynamicValue } from "utils/DynamicBindingUtils";
|
||||
import { ArrayComponent } from "./ArrayComponent";
|
||||
import type { ControlData, ControlProps } from "./BaseControl";
|
||||
import BaseControl from "./BaseControl";
|
||||
|
||||
class ArrayControl extends BaseControl<ControlProps> {
|
||||
render() {
|
||||
return (
|
||||
<ArrayComponent
|
||||
items={this.props.propertyValue}
|
||||
updateItems={this.updateItems}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
updateItems = (items: string[], isUpdatedViaKeyboard = false) => {
|
||||
this.updateProperty(this.props.propertyName, items, isUpdatedViaKeyboard);
|
||||
};
|
||||
|
||||
static getControlType() {
|
||||
return "ARRAY_INPUT";
|
||||
}
|
||||
|
||||
static canDisplayValueInUI(_config: ControlData, value: string): boolean {
|
||||
if (isDynamicValue(value)) return false;
|
||||
|
||||
try {
|
||||
const items: DropdownOption[] = JSON.parse(value);
|
||||
|
||||
for (const x of items) {
|
||||
const keys = objectKeys(x);
|
||||
|
||||
if (!keys.includes("label") || !keys.includes("value")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export default ArrayControl;
|
||||
|
|
@ -76,12 +76,14 @@ import type { IconSelectControlV2Props } from "./IconSelectControlV2";
|
|||
import IconSelectControlV2 from "./IconSelectControlV2";
|
||||
import PrimaryColumnsControlWDS from "./PrimaryColumnsControlWDS";
|
||||
import ToolbarButtonListControl from "./ToolbarButtonListControl";
|
||||
import ArrayControl from "./ArrayControl";
|
||||
|
||||
export const PropertyControls = {
|
||||
InputTextControl,
|
||||
DropDownControl,
|
||||
SwitchControl,
|
||||
OptionControl,
|
||||
ArrayControl,
|
||||
CodeEditorControl,
|
||||
DatePickerControl,
|
||||
ActionSelectorControl,
|
||||
|
|
|
|||
|
|
@ -7,4 +7,6 @@ export const defaultsConfig = {
|
|||
widgetType: "AI_CHAT",
|
||||
version: 1,
|
||||
responsiveBehavior: ResponsiveBehavior.Fill,
|
||||
initialAssistantMessage: "",
|
||||
initialAssistantSuggestions: [],
|
||||
} as unknown as WidgetDefaultProps;
|
||||
|
|
|
|||
|
|
@ -79,16 +79,27 @@ export const propertyPaneContent = [
|
|||
defaultValue: "",
|
||||
},
|
||||
{
|
||||
helpText: "Configures a prompt for the assistant",
|
||||
propertyName: "systemPrompt",
|
||||
label: "Prompt",
|
||||
helpText: "Configures an initial assistant message",
|
||||
propertyName: "initialAssistantMessage",
|
||||
label: "Initial Assistant Message",
|
||||
controlType: "INPUT_TEXT",
|
||||
isJSConvertible: false,
|
||||
isBindProperty: false,
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.TEXT },
|
||||
defaultValue: "",
|
||||
},
|
||||
{
|
||||
helpText: "Configures initial assistant suggestions",
|
||||
propertyName: "initialAssistantSuggestions",
|
||||
label: "Initial Assistant Suggestions",
|
||||
controlType: "ARRAY_INPUT",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.ARRAY },
|
||||
defaultValue: [],
|
||||
},
|
||||
{
|
||||
helpText: "Controls the visibility of the widget",
|
||||
propertyName: "isVisible",
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -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: [] as Message[],
|
||||
prompt: "",
|
||||
isWaitingForResponse: false,
|
||||
};
|
||||
|
|
@ -123,13 +108,85 @@ class WDSAIChatWidget extends BaseWidget<WDSAIChatWidgetProps, State> {
|
|||
return {};
|
||||
}
|
||||
|
||||
adaptMessages(messages: Message[]): ChatMessage[] {
|
||||
return messages.map((message) => ({
|
||||
...message,
|
||||
isAssistant: message.role === "assistant",
|
||||
}));
|
||||
componentDidMount() {
|
||||
// Add initial assistant message with suggestions if they were configured
|
||||
if (this.props.initialAssistantMessage.length > 0) {
|
||||
this.setState((state) => ({
|
||||
...state,
|
||||
messages: [
|
||||
{
|
||||
id: Math.random().toString(),
|
||||
content: this.props.initialAssistantMessage,
|
||||
role: "assistant",
|
||||
promptSuggestions: this.props.initialAssistantSuggestions || [],
|
||||
},
|
||||
],
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: WDSAIChatWidgetProps): void {
|
||||
// Track changes in the widget's properties and update the local state accordingly
|
||||
|
||||
// Update the initial assistant message
|
||||
if (
|
||||
prevProps.initialAssistantMessage !==
|
||||
this.props.initialAssistantMessage ||
|
||||
prevProps.initialAssistantSuggestions !==
|
||||
this.props.initialAssistantSuggestions
|
||||
) {
|
||||
let updatedMessage: Message | null;
|
||||
|
||||
//
|
||||
if (this.props.initialAssistantMessage.length > 0) {
|
||||
const currentMessage = this.state.messages[0];
|
||||
|
||||
updatedMessage = {
|
||||
// If the initial assistant message is set, update it
|
||||
// Otherwise, create a new one
|
||||
...(currentMessage || {
|
||||
id: Math.random().toString(),
|
||||
role: "assistant",
|
||||
}),
|
||||
content: this.props.initialAssistantMessage,
|
||||
promptSuggestions: this.props.initialAssistantSuggestions,
|
||||
};
|
||||
} else {
|
||||
updatedMessage = null;
|
||||
}
|
||||
|
||||
this.setState((state) => ({
|
||||
...state,
|
||||
messages: updatedMessage ? [updatedMessage] : [],
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
|
|
@ -148,18 +205,7 @@ class WDSAIChatWidget extends BaseWidget<WDSAIChatWidgetProps, State> {
|
|||
}),
|
||||
() => {
|
||||
const messages: Message[] = [...this.state.messages];
|
||||
|
||||
if (this.props.systemPrompt) {
|
||||
messages.unshift({
|
||||
id: String(Date.now()),
|
||||
content: this.props.systemPrompt,
|
||||
role: "system",
|
||||
});
|
||||
}
|
||||
|
||||
const params = {
|
||||
messages,
|
||||
};
|
||||
const params = { messages };
|
||||
|
||||
this.executeAction({
|
||||
triggerPropertyName: "onClick",
|
||||
|
|
@ -182,6 +228,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,
|
||||
|
|
@ -190,7 +238,11 @@ class WDSAIChatWidget extends BaseWidget<WDSAIChatWidgetProps, State> {
|
|||
};
|
||||
|
||||
handlePromptChange = (prompt: string) => {
|
||||
this.setState({ prompt });
|
||||
this.updatePrompt(prompt);
|
||||
};
|
||||
|
||||
handleApplyAssistantSuggestion = (suggestion: string) => {
|
||||
this.updatePrompt(suggestion);
|
||||
};
|
||||
|
||||
getWidgetView(): ReactNode {
|
||||
|
|
@ -199,6 +251,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}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user