Skip to content

Commit

Permalink
chore: Update docs site and README with addition usage info (#13)
Browse files Browse the repository at this point in the history
* chore: Update docs site and README with information on useChat hook

* Add information on how to use component with SSR frameworks

* Address comments
  • Loading branch information
mrderyk authored Feb 28, 2024
1 parent 7ed08f9 commit 7edc62c
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 23 deletions.
135 changes: 121 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,30 @@ React-Chatbot is a UI widget for adding [Vectara](https://vectara.com/)-powered

## UI

_Screenshots coming soon!_
React-Chatbot adds a button to the lower right corner of your application:

<img alt="Chatbot toggle button" src="images/toggleButton.png" width=300></img>

When the button is clicked, a chat window opens, where your users can start a conversation with your data:

<img alt="Initial chat window state" src="images/emptyMessagesState.png" width=350></img>

The Vectara platform responds to user messages and presents a list of references that support its answer:

<img alt="Chat message answer" src="images/answerWithReferences.png" width=350></img>

When the chat API returns an error, the UI allows the user to retry sending the message:

<img alt="Retry message" src="images/messageWithError.png" width=350></img>

React-Chatbot is also ready for use on mobile sites, taking the fullscreen when the chat window is opened:

<img alt="Mobile-friendly chat window" src="images/responsiveDemo.gif" width=350></img>

## Use it in your application

### Use the chatbot directly

Install React-Chatbot:

```shell
Expand All @@ -40,61 +60,148 @@ import { ReactChatbot } from "@vectara/react-chatbot";

<ReactChatbot
customerId="CUSTOMER_ID"
corpusId="CORPUS_ID"
corpusId={["CORPUS_ID_1", "CORPUS_ID_2", "CORPUS_ID_N"]}
apiKey="API_KEY"
title="My Chatbot"
placeholder="Chat with your AI assistant"
inputSize="large"
emptyStateDisplay={<MyEmptyStateDisplayComponent />}
isInitiallyOpen={false}
zIndex={ /* (optional) number representing the z-index the component should have */ }
/>;
```

### Configuration options
#### <u>Configuration Options</u>

#### `customerId` (required)
##### `customerId` (required)

Every Vectara account is associated with a customer ID. You can find your customer ID by logging into the [Vectara Console](https://console.vectara.com/) and opening your account dropdown in the top-right corner.

#### `corpusIds` (required)
##### `corpusIds` (required)

After you [create a corpus](https://docs.vectara.com/docs/console-ui/creating-a-corpus), you can find its ID by navigating to the corpus and looking in the top-left corner, next to the corpus name.

#### `apiKey` (required)
##### `apiKey` (required)

API keys enable applications to access data inside of corpora. Learn how to [create a **QueryService** API key](https://docs.vectara.com/docs/console-ui/manage-api-access#create-an-api-key).

#### `title` (optional)
##### `title` (optional)

Configure the title in the header of the chatbot window.

#### `placeholder` (optional)
##### `placeholder` (optional)

Configure the placeholder text in the chatbot's input.

#### `emptyStateDisplay` (optional)
##### `emptyStateDisplay` (optional)

Configure JSX content to render in the messages window when there are no messages to display.

#### `isInitiallyOpen` (optional)
##### `isInitiallyOpen` (optional)

Set the chat window to be opened on initial render.

#### `inputSize` (optional)
##### `inputSize` (optional)

Control the size of the input - either "large" (18px) or "medium" (14px)

##### `zIndex` (optional)

Customize the z-index of the chatbot widget

### Use your own views with the useChat hook

Install React-Chatbot:

```shell
npm install --save @vectara/react-chatbot
```

Then use the `useChat` hook in your application like this:

```js
import { useChat } from "@vectara/react-chatbot/lib";

/* snip */

const { sendMessage, messageHistory, isLoading, hasError } = useChat(
"CUSTOMER_ID",
["CORPUS_ID_1", "CORPUS_ID_2", "CORPUS_ID_N"],
"API_KEY"
);
```

The values returned by the hook can be passed on to your custom components as props or used in any way you wish.

#### <u>Hook Values</u>

##### sendMessage: `async ({ query: string; isRetry?: boolean }) => void;`

This is used to send a message to the chat API. Send `true` for the optional `isRetry` flag to if this is retrying a previously failed message. This allows the internal logic to correctly link the next successful answer to the failed query.

##### messageHistory: `ChatTurn[]`

This is an array of objects representing question and answer pairs from the entire conversation. Each pair is a `ChatTurn` object. More information on types can be found [here](src/types.ts).

##### isLoading: `boolean`

A boolean value indicating whether or not a chat API request is still pending

Used to control the size of the input - either "large" (18px) or "medium" (14px)
##### hasError: `boolean`

A boolean value indicating whether or not the most recent chat API request encountered an error

### Usage with SSR Frameworks

Using React-Chatbot with SSR frameworks may require additional infrastucture. Here are some common gotchas:

#### Next.js

Since React-Chatbot requires a few browser-only features to function, we need to defer the rendering of the component to the client. In order to do this, you will need to:

1. Use the `"use client"` directive in the file that imports ReactChatbot.
2. Use a one-time `useEffect` call in ReactChatbot's consumer. The useEffect callback should set the rendered widget as a state on the consumer component.
3. Include the rendered widget state value in the rendered consumer component.

Example:

```
"use client";
export const App = (props: Props): ReactNode => {
const [chatWidget, setChatWidget] = useState<ReactNode>(null);
/* the rest of your code */
useEffect(() => {
setChatWidget(
<ReactChatbot
customerId="CUSTOMER_ID"
corpusIds={["CORPUS_ID_1", "CORPUS_ID_2", "CORPUS_ID_N"]}
apiKey="API_KEY"
/>
);
}, []);
return (
<>
{ /* other content */ }
</>
)
};
```

### Set up your data

React-Chat uses the data in your Vectara corpus to provide factual responses. To set this up:
React-Chatbot uses the data in your Vectara corpus to provide factual responses. To set this up:

1. [Create a free Vectara account](https://console.vectara.com/signup).
2. [Create a corpus and add data to it](https://docs.vectara.com/docs/console-ui/creating-a-corpus).
3. [Create a **QueryService** API key](https://docs.vectara.com/docs/console-ui/manage-api-access#create-an-api-key).

**Pro-tip:** After you create an API key, navigate to your corpus and click on the "Access control" tab. Find your API key on the bottom and select the "Copy all" option to copy your customer ID, corpus ID, and API key. This gives you all the data you need to configure your `<ReactChat />` instance.

![Copy all option](https://raw.githubusercontent.com/vectara/react-chat/main/images/copyAll.jpg)
![Copy all option](https://raw.githubusercontent.com/vectara/react-chatbot/main/images/copyAll.jpg)

## Maintenance

Expand Down
61 changes: 60 additions & 1 deletion docs/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ const App = () => {
<VuiText>
<p>
For help,{" "}
<VuiLink isAnchor href="https://github.com/vectara/react-chat">
<VuiLink isAnchor href="https://github.com/vectara/react-chatbot">
read the docs.
</VuiLink>
</p>
Expand All @@ -236,6 +236,65 @@ const App = () => {
{generateCodeSnippet(customerId, corpusIds, apiKey, title, placeholder, inputSize, emptyStateJsx)}
</VuiCode>

<VuiSpacer size="xxl" />

<VuiTitle size="m">
<h2>Create your own view</h2>
</VuiTitle>

<VuiSpacer size="m" />

<VuiText>
<p>
React-Chatbot also exposes a useChat hook that sends and receives data to/from the chat API. This is
perfect for rolling your own components that are powered by Vectara's chat functionality.
</p>
<p>Check out the example below.</p>
</VuiText>

<VuiSpacer size="s" />

<VuiCode language="tsx">
{`
import { useChat } from "@vectara/react-chatbot/lib";
export const App = () => {
const { sendMessage, messageHistory, isLoading, hasError } = useChat(
DEFAULT_CUSTOMER_ID,
DEFAULT_CORPUS_IDS,
DEFAULT_API_KEY
);
/* You can pass the values returned by the hook to your custom components as props, or use them
however you wish. */
};
`}
</VuiCode>

<VuiSpacer size="m" />

<VuiText>
<p></p>
<p>The hook returns:</p>
<ul>
<li>sendMessage - a function that sends a string to the Chat API endpoint</li>
<li>messageHistory - an array of objects representing messages from the entire conversation</li>
<li>isLoading - a boolean value indicating whether or not a chat message request is pending</li>
<li>
hasError - a boolean value indicating whether or not the previous message request returned an error
</li>
</ul>
</VuiText>

<VuiSpacer size="m" />

<VuiText>
For more details, including return value types,{" "}
<VuiLink isAnchor href="https://github.com/vectara/react-chatbot">
read the docs.
</VuiLink>
</VuiText>

<ConfigurationDrawer
isOpen={isConfigurationDrawerOpen}
onClose={() => setIsConfigurationDrawerOpen(false)}
Expand Down
Binary file added images/answerWithReferences.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/emptyMessagesState.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/messageWithError.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/responsiveDemo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/toggleButton.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions src/components/ChatItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,11 @@ export const ChatItem = ({ question, answer, searchResults, onRetry }: Props) =>
content = (
<div className="vrcbChatMessageContainer vrcbChatMessageContainer--error">
<VuiSpacer size="m" />
<VuiFlexContainer alignItems="center">
<VuiFlexContainer alignItems="center" spacing="none">
<VuiFlexContainer alignItems="center" spacing="xxs">
<VuiFlexItem grow={false} shrink={true}>
<Error />
</VuiFlexItem>

<VuiFlexItem grow={false}>Message not sent.</VuiFlexItem>
</VuiFlexContainer>
{onRetry && (
Expand Down
6 changes: 4 additions & 2 deletions src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const ChatView = ({
}: Props) => {
const [isOpen, setIsOpen] = useState<boolean>(isInitiallyOpen ?? false);
const [query, setQuery] = useState<string>("");
const { sendMessage, messageHistory, isLoading, error } = useChat(customerId, corpusIds, apiKey);
const { sendMessage, messageHistory, isLoading, hasError } = useChat(customerId, corpusIds, apiKey);
const appLayoutRef = useRef<HTMLDivElement>(null);
const isScrolledToBottomRef = useRef(true);

Expand Down Expand Up @@ -98,7 +98,9 @@ export const ChatView = ({
const chatItems = messageHistory.map((turn, index) => {
const { question, answer, results } = turn;
const onRetry =
error && index === messageHistory.length - 1 ? () => sendMessage({ query: question, isRetry: true }) : undefined;
hasError && index === messageHistory.length - 1
? () => sendMessage({ query: question, isRetry: true })
: undefined;

return <ChatItem key={index} question={question} answer={answer} searchResults={results} onRetry={onRetry} />;
});
Expand Down
8 changes: 4 additions & 4 deletions src/useChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const useChat = (customerId: string, corpusIds: string[], apiKey: string)
const [recentAnswer, setRecentAnswer] = useState<ChatTurn | null>();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [conversationId, setConversationId] = useState<string | undefined>();
const [error, setError] = useState<boolean>(false);
const [hasError, setHasError] = useState<boolean>(false);
const getLanguage = (languageValue?: string): SummaryLanguage => (languageValue ?? "eng") as SummaryLanguage;

const sendMessage = async ({ query, isRetry = false }: { query: string; isRetry?: boolean }) => {
Expand All @@ -34,7 +34,7 @@ export const useChat = (customerId: string, corpusIds: string[], apiKey: string)
];
});
} else {
setError(false);
setHasError(false);
}

const baseSearchRequestParams = {
Expand All @@ -60,7 +60,7 @@ export const useChat = (customerId: string, corpusIds: string[], apiKey: string)
try {
initialSearchResponse = await sendSearchRequest(baseSearchRequestParams);
} catch (error) {
setError(true);
setHasError(true);
setIsLoading(false);
return [];
}
Expand Down Expand Up @@ -109,6 +109,6 @@ export const useChat = (customerId: string, corpusIds: string[], apiKey: string)
sendMessage,
messageHistory,
isLoading,
error
hasError
};
};

0 comments on commit 7edc62c

Please sign in to comment.