<AIChatLog><AIChatMessage variant="user"><AIChatMessageAuthor aria-label="You said at 2:36pm">Gibby Radki</AIChatMessageAuthor><AIChatMessageBody>What does the SMS delivery error code 30003 mean?</AIChatMessageBody></AIChatMessage><AIChatMessage variant="bot"><AIChatMessageAuthor aria-label="AI said">Good Bot</AIChatMessageAuthor><AIChatMessageBody>Here is what I found, error code 30003 means: The destination phone is unavailable or turned off, or it may be a landline or phone that doesn't support SMS.</AIChatMessageBody></AIChatMessage></AIChatLog>
An AI Chat Log is a way to display conversations between a user and AI. If you are looking for a chat between 2 or more humans, please refer to Chat Log.
The AI Chat Log package includes these main components:
- AIChatLog
- AIChatMessage
- AIChatMessageAuthor
- AIChatMessageBody
- AIChatMessageActionGroup
- AIChatMessageActionCard
- AIChatMessageLoading
To ensure the chat is accessible, only use the AI Chat components within an AIChatLog
component and use AIChatMessage
to wrap AIChatMessageBody
, AIChatMessageActionGroup
and components together.
The only other accessibility requirement is providing the AIChatMessageActionCard
a descriptive label via the aria-label
React prop.
The AIChatLog component has role="log"
which means that any new messages added to it are announced by assistive technology.
A message must include the author and body. Any message text from a user or a bot must be contained within the AIChatMessageBody
component. Due to lengthy AI responses, the chat layout is top-down.
const BasicMessage = () => {return (<AIChatLog><AIChatMessage variant="bot"><AIChatMessageAuthor aria-label="AI said">Good Bot</AIChatMessageAuthor><AIChatMessageBody>Here is what I found, error code 30003 means: The destination phone is unavailable or turned off, or it may be a landline or phone that doesn't support SMS.</AIChatMessageBody></AIChatMessage></AIChatLog>);};render(<BasicMessage />)
const BasicMessage = () => {return (<AIChatLog><AIChatMessage variant="user"><AIChatMessageAuthor aria-label="You said at 2:36pm">Gibby Radki</AIChatMessageAuthor><AIChatMessageBody>I would like some information on twilio error codes for undelivered messages</AIChatMessageBody></AIChatMessage></AIChatLog>);};render(<BasicMessage />)
The AIChatMessageBody
component has two sizes, size="default"
and size="fullScreen"
. The fullScreen size is used where the ChatLog is displayed in the full width of the page where larger font size is needed.
<AIChatLog><AIChatMessage variant="user"><AIChatMessageAuthor aria-label="You said">Gibby Radki</AIChatMessageAuthor><AIChatMessageBody size="default">I'm a message that should be displayed in compact elements</AIChatMessageBody></AIChatMessage><AIChatMessage variant="user"><AIChatMessageAuthor aria-label="You said">Gibby Radki</AIChatMessageAuthor><AIChatMessageBody size="fullScreen">I'm a message that will be displayed in full screen width</AIChatMessageBody></AIChatMessage></AIChatLog>
Message actions can be used to provide quick responses or actions to the user.
AIChatMessageActionGroup
should be a child of AIChatMessage
so that the text and meta information are correctly grouped together for assistive technologies. AIChatMessageActionCard
also needs a readable aria-label
that summarizes what the meta information says.
Each item within AIChatMessageActionGroup
should be wrapped with AIChatMessageActionCard
. It is recommended to use reset button variants for content within AIChatMessageActionCard
.
Actions can still be added in AIChatMessageBody
which are returned from the AI response.
const MessageWithFeedback = () => {return (<AIChatLog><AIChatMessage variant="bot"><AIChatMessageAuthor aria-label="AI said">Good Bot</AIChatMessageAuthor><AIChatMessageBody>Here is what I found, error code 30003 means: The destination phone is unavailable or turned off, or it may be a landline or phone that doesn't support SMS.</AIChatMessageBody><AIChatMessageActionGroup><AIChatMessageActionCard aria-label="Feedback form">Is this helpful?<Button variant="reset" size="reset" aria-label="this is a helpful response"><ThumbsUpIcon decorative={false} title="like result" /></Button><Button variant="reset" size="reset"><ThumbsDownIcon decorative={false} title="dislike result" aria-label="this is not a helpful response" /></Button></AIChatMessageActionCard><AIChatMessageActionCard aria-label="Rewrite and copy buttons"><Button variant="reset" size="reset"><RefreshIcon decorative={true}/> Rewrite</Button><Button variant="reset" size="reset"><CopyIcon decorative={true}/> Copy</Button></AIChatMessageActionCard></AIChatMessageActionGroup></AIChatMessage></AIChatLog>);};render(<MessageWithFeedback />)
const MessageWithFeedback = () => {return (<AIChatLog><AIChatMessage variant="bot"><AIChatMessageAuthor aria-label="AI said">Good Bot</AIChatMessageAuthor><AIChatMessageBody><Paragraph>Below is a list of actions that can be taken with flex wrapping supported:</Paragraph><ButtonGroup><Button variant="secondary" size="rounded_small" onClick={() => {}} >View Logs</Button><Button variant="secondary" size="rounded_small" onClick={() => {}}>Run Diagnostics</Button><Button variant="secondary" size="rounded_small" onClick={() => {}}>Submit Bug Report</Button></ButtonGroup></AIChatMessageBody></AIChatMessage></AIChatLog>);};render(<MessageWithFeedback />)
Use the AIChatMessageLoading
component to indicate that the bot is typing or processing a response. During this time no user input should be accepted. No new messages should be added to a chat until the AI operation is finished processing.
The SkeletonLoader lengths vary on each render to give a more natural pending message body interaction.
const MessageWithLoading = () => {return (<AIChatLog><AIChatMessage variant="bot"><AIChatMessageAuthor aria-label="AI said" bot>Good Bot</AIChatMessageAuthor><AIChatMessageBody><AIChatMessageLoading /></AIChatMessageBody></AIChatMessage></AIChatLog>);};render(<MessageWithLoading />)
const MessageWithLoadingAndStop = () => {return (<AIChatLog><AIChatMessage variant="bot"><AIChatMessageAuthor aria-label="AI said" bot>Good Bot</AIChatMessageAuthor><AIChatMessageBody><AIChatMessageLoading onStopLoading={() => {}} /></AIChatMessageBody></AIChatMessage></AIChatLog>);};render(<MessageWithLoadingAndStop />)
AIChatMessageAuthor
can utilize custom icons by passing an icon to the prop avatarIcon
or an image to the avatarSrc
prop.
const AvatarExample = () => {return (<AIChatLog><AIChatMessage variant="user"><AIChatMessageAuthor avatarIcon={LogoTwilioIcon} aria-label="You said">Gibby Radki</AIChatMessageAuthor></AIChatMessage><AIChatMessage variant="user"><AIChatMessageAuthor avatarSrc={Logo.src} aria-label="You said">Gibby Radki</AIChatMessageAuthor></AIChatMessage></AIChatLog>);};render(<AvatarExample />)
This example combines all the separate features displayed previously into one example. It shows how all the features work together harmoniously through composition.
const AIChatLogExample = () => {return (<AIChatLog><AIChatMessage variant="user"><AIChatMessageAuthor aria-label="You said">Gibby Radki</AIChatMessageAuthor><AIChatMessageBody>Hi, I'm getting errors codes when sending an SMS.</AIChatMessageBody></AIChatMessage><AIChatMessage variant="bot"><AIChatMessageAuthor aria-label="AI said">Good Bot</AIChatMessageAuthor><AIChatMessageBody><Paragraph>Error codes can be returned from various parts of the process. What error codes are you encountering?</Paragragh><ButtonGroup><Button variant="secondary" size="rounded_small" onClick={() => {}} >21608</Button><Button variant="secondary" size="rounded_small" onClick={() => {}}>30007</Button><Button variant="secondary" size="rounded_small" onClick={() => {}}>30009</Button></ButtonGroup></AIChatMessageBody></AIChatMessage><AIChatMessage variant="bot"><AIChatMessageAuthor aria-label="AI said">Good Bot</AIChatMessageAuthor><AIChatMessageBody>Error 21608 means you're trying to send a message from an unverified number. Is your number verified in your Twilio account?</AIChatMessageBody><AIChatMessageActionGroup><AIChatMessageActionCard aria-label="Feedback form">Is this helpful?<Button variant="reset" size="reset" aria-label="this is a helpful response"><ThumbsUpIcon decorative={false} title="like result" /></Button><Button variant="reset" size="reset"><ThumbsDownIcon decorative={false} title="dislike result" aria-label="this is not a helpful response"/></Button></AIChatMessageActionCard></AIChatMessageActionGroup></AIChatMessage><AIChatMessage variant="user"><AIChatMessageAuthor aria-label="You said" bot>Gibby Radki</AIChatMessageAuthor><AIChatMessageBody>No, how do I verify it?</AIChatMessageBody></AIChatMessage><AIChatMessage variant="bot"><AIChatMessageAuthor aria-label="AI said" bot>Good Bot</AIChatMessageAuthor><AIChatMessageBody><AIChatMessageLoading onStopLoading={() => {}} /></AIChatMessageBody></AIChatMessage></AIChatLog>);};render(<AIChatLogExample />)
The useAIChatLogger
hook provides a hook-based approach to managing AI chat state. It is best used with the <AIChatLogger />
component.
useAIChatLogger
returns 4 things:
- An array of
aiChats
. - A
push
method used to add a chat, optionally with a custom ID. - A
pop
method used to remove a chat, optionally via its ID. - A
clear
method used to remove all chats.
The <AIChatLogger />
component handles rendering the chats it is passed via props. It handles how chats enter and leave the UI.
const { aiChats } = useAIChatLogger();
return <AIChatLogger aiChats={aiChats} />;
You can push or pop a chat based on an action or event. In this example it's based on a button click:
const aiChatFactory = ([ message, variant, metaLabel, meta ]) => {const time = new Date(0).toLocaleString('en-US',{ hour: 'numeric', minute: 'numeric', timeZone: 'UTC', hour12: true })return {variant,content: (<AIChatMessage variant={variant}><AIChatMessageAuthor aria-label={metaLabel + time}>{meta}</AIChatMessageAuthor><AIChatMessageBody>{message}</AIChatMessageBody></AIChatMessage>)}};const chatTemplates = [["Hello", "user", "You said at ", "Gibby Radki"],["Hi there", "bot", "AI said at ", "Good Bot"],["Greetings", "user", "You said at ", "Gibby Radki"],["Good to meet you", "bot", "AI said at ", "Good Bot"]];const AIChatLoggerExample = () => {const [templateIdx, setTemplateIdx] = React.useState(2);const { aiChats, push, pop, clear } = useAIChatLogger(aiChatFactory(chatTemplates[0]),aiChatFactory(chatTemplates[1]));const [loading, setLoading] = React.useState(false);const pushChat = () => {const template = chatTemplates[templateIdx];setTemplateIdx((idx) => ++idx % chatTemplates.length);const chat = aiChatFactory(template);if (template[1] === "bot") {const id = uid(chat.content);setLoading(true);push({id,variant: template[1],content: (<AIChatMessage variant="bot"><AIChatMessageAuthor aria-label="AI said">Good Bot</AIChatMessageAuthor><AIChatMessageBody><AIChatMessageLoading /></AIChatMessageBody></AIChatMessage>),});setTimeout(() => {pop(id);setLoading(false);push(chat);}, 1000);} else {push(chat);}}const popChat = () => {pop();setTemplateIdx((idx) => idx === 0 ? idx : --idx % chatTemplates.length);}return(<Stack orientation="vertical"><ButtonGroup><Button variant="primary" disabled={loading} onClick={pushChat}>Push Chat</Button><Button variant="primary" disabled={loading} onClick={popChat}>Pop Chat</Button><Button variant="primary" disabled={loading} onClick={clear}>Clear Chat</Button></ButtonGroup><AIChatLogger aiChats={aiChats} /></Stack>)}render(<AIChatLoggerExample />);
Keep any generated responses from the AI contained in the AIChatMessageBody
component. Each chat message should only have one AIChatMessageBody
component.