Using AI chat as a web component
The web component for AI chat contains helpful facades to the underlying class for ease of use.
Contents
Overview
Installation
Accessing instance methods
User defined responses
Overview
AI chat shows two web components that act as a facade in front of the core AI chat.
To use the float
layout, refer to cds-aichat-container. If you want to use a custom size, such as rendering in a sidebar, full-screen mode, or nested within your UI, refer to cds-aichat-custom-element.
The web components provide helpful alternate ways to manage user_defined
responses, writeable elements, and the AI chat rendering cycle. You can review Using the API to understand the references.
For more information, see visit the examples page for more examples.
Installation
Install by using npm
:
npm install @carbon/ai-chat
Or using yarn
:
yarn add @carbon/ai-chat
Basic example
Render this component in your application, and provide the configuration options for the AI chat as a prop.
import '@carbon/ai-chat/dist/es/cds-aichat-container.js';
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
const config = {
// Your configuration object.
}
@customElement('my-app')
export class MyApp extends LitElement {
render() {
return html`<cds-aichat-container .config={config} />`;
}
}
Using cds-aichat-container
The cds-aichat-container
component loads and renders an instance of the AI chat when it mounts and deletes that instance when unmounted. If option changes occur in the AI chat configuration, it also deletes the previous AI chat and creates a new one with the new configuration.
Note: This component calls the instance.render()
method for you. You do not need to call it yourself. You can use the onBeforeRender
or onAfterRender
properties to execute operations before or after the render calls. Refer to the following table.
Props
cds-aichat-container
has the following props.
Attribute | Required | Type | Description |
---|---|---|---|
config | Yes | object | The options object for configuring the chat. |
onBeforeRender | No | function | The AI chat calls this callback function after loading, but before calling the render function. It passes a single argument, the loaded instance of the AI chat. To use the available instance methods, you can use this function to obtain a reference to the AI chat instance. |
onAfterRender | No | function | The AI chat calls this callback function after loading and after calling the render function. It passes a single argument, the loaded instance of the AI chat. To use the available instance methods, you can use this function to obtain a reference to the AI chat instance. |
Using cds-aichat-custom-element
This library provides the component cds-aichat-custom-element
, which you can use to render the AI chat inside a custom element. It is necessary to change the location where the AI chat renders.
This component's default behavior adds and removes a class from the main window of the AI chat. It also applies the same behavior to your custom element to manage the visibility of the AI chat when it opens or closes. When the AI chat closes, it adds a classname to the AI chat main window to hide the element. Another classname is added to your custom element to set its width and height to 0, so that it doesn't take up space.
Note: The custom element must remain visible if you want to use the built-in AI chat launcher, which is also contained in your custom element.
If you don't want these behaviors, then provide your own onViewChange
prop to cds-aichat-custom-element
and provide your own logic for controlling the visibility of the AI chat. If you want custom animations when the AI chat opens and closes, use this mechanism to do that. Refer to the following example.
import '@carbon/ai-chat/dist/es/cds-aichat-custom-element.js';
import { css, html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
const config = {
// Your configuration object.
}
@customElement('my-app')
export class MyApp extends LitElement {
static styles = css` .fullscreen { height: 100vh; width: 100vw; }`;
render() {
return html`<cds-aichat-custom-element class="fullscreen" .config={config} />`;
}
}
cds-aichat-custom-element
inherits all props from cds-aichat-container
. Refer to the following table for optional props..
Attribute | Type | Description |
---|---|---|
onViewChange | function | A custom element controlling the visiblity of the AI chat's main window requires an optional listener for "view:change" events. If you do not provide a callback, the default one employs certain classnames to manage the AI chat and the custom element. You can provide a different callback if you want custom behavior, such as an animation when the main window opens or closes. |
Note: Provide this function before you load the AI chat. The event handler does not update after the AI chat loads.
Accessing instance methods
You can use the onBeforeRender
or onAfterRender
props to access the AI chat’s instance if you need to call instance methods later. This example renders a button that toggles the AI chat open and is only rendered after the instance becomes available. Refer to the following example.
import '@carbon/ai-chat/dist/es/cds-aichat-container.js';
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import { type ChatInstance } from '@carbon/ai-chat';
const config = {
// Your configuration object.
}
@customElement('my-app')
export class MyApp extends LitElement {
@state()
_instance: ChatInstance;
onBeforeRender = (instance: ChatInstance) => {
this._instance = instance;
// Do whatever you want to do with the instance.
}
render() {
return html`<cds-aichat-container .config=${config} .onBeforeRender=${this.onBeforeRender} />${ this._instance ? '<span>Instance loaded</span>' : ''}`;
}
}
User defined responses
This component is also capable of managing user defined responses. The AI chat throws events when it receives a user_defined
response from your custom backend. These events come with the name of a dynamically generated slot.
Then, you dynamically generate these slots to pass into the AI chat's web component and pass in your custom content to be displayed in the correct slot inside the AI chat. Refer to the following example.
import '@carbon/ai-chat/dist/es/cds-aichat-container.js';
import {
BusEventType,
type ChatInstance,
type GenericItem,
type MessageResponse,
type PublicConfig,
type UserDefinedItem,
} from '@carbon/ai-chat';
import { html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { customSendMessage } from './customSendMessage';
interface UserDefinedSlotsMap {
[key: string]: UserDefinedSlot;
}
interface UserDefinedSlot {
message: GenericItem;
fullMessage: MessageResponse;
}
const config: PublicConfig = {
messaging: {
customSendMessage,
},
};
@customElement('my-app')
export class Demo extends LitElement {
@state()
accessor instance!: ChatInstance;
@state()
accessor userDefinedSlotsMap: UserDefinedSlotsMap = {};
onBeforeRender = (instance: ChatInstance) => {
this.instance = instance;
instance.on({ type: BusEventType.USER_DEFINED_RESPONSE, handler: this.userDefinedHandler });
};
/**
* Each user defined event is tied to a slot deeply rendered with-in AI chat that is generated at runtime.
* Here we make sure we store all these slots along with their relevant data in order to be able to dynamically
* render the content to be slotted when this.renderUserDefinedSlots() is called in the render function.
*
* @see https://web-chat.global.assistant.watson.cloud.ibm.com/carbon-chat.html?to=api-render#user-defined-responses
*/
userDefinedHandler = (event: any) => {
const { data } = event;
this.userDefinedSlotsMap[data.slot] = {
message: data.message,
fullMessage: data.fullMessage,
};
this.requestUpdate();
};
/**
* This renders each of the dynamically generated slots that were generated by the AI chat by calling
* this.renderUserDefinedResponse on each one.
*
* @see https://web-chat.global.assistant.watson.cloud.ibm.com/carbon-chat.html?to=api-render#user-defined-responses
*/
renderUserDefinedSlots() {
const userDefinedSlotsKeyArray = Object.keys(this.userDefinedSlotsMap);
return userDefinedSlotsKeyArray.map(slot => {
return this.renderUserDefinedResponse(slot);
});
}
/**
* Here we process a single item from this.userDefinedSlotsMap. We go ahead and use a switch statement to decide
* which element we should be rendering.
*
* @see https://web-chat.global.assistant.watson.cloud.ibm.com/carbon-chat.html?to=api-render#user-defined-responses
*/
renderUserDefinedResponse(slot: keyof UserDefinedSlotsMap) {
const { message, fullMessage } = this.userDefinedSlotsMap[slot];
const userDefinedMessage = message as UserDefinedItem;
// Check the "type" we have used as our key.
switch (userDefinedMessage.user_defined?.user_defined_type) {
case 'my_unique_identifier':
// And here is an example using your own component.
return html`<div slot=${slot}>${userDefinedMessage.user_defined.text as string}></div>`;
default:
return null;
}
}
render() {
return html`
<h1>Welcome!</h1>
<cds-aichat-container .onBeforeRender=${this.onBeforeRender} .config=${config}
>${this.renderUserDefinedSlots()}</cds-aichat-container
>
`;
}
}