<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
pushmethod used to add a chat, optionally with a custom ID. - A
popmethod used to remove a chat, optionally via its ID. - A
clearmethod 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.