+ ${this.renderChatThread(this.chatHistoryController.chatHistory)}
+
+
`
+ : ''}
+ ${this.renderChatThread(this.chatThread)}
`
: ''}
${this.chatController.isAwaitingResponse
diff --git a/packages/chat-component/src/components/chat-history-controller.ts b/packages/chat-component/src/components/chat-history-controller.ts
new file mode 100644
index 00000000..6d0ce32a
--- /dev/null
+++ b/packages/chat-component/src/components/chat-history-controller.ts
@@ -0,0 +1,81 @@
+import { type ReactiveController, type ReactiveControllerHost } from 'lit';
+import { html } from 'lit';
+import { globalConfig, MAX_CHAT_HISTORY } from '../config/global-config.js';
+
+import iconHistory from '../../public/svg/history-icon.svg?raw';
+import iconHistoryDismiss from '../../public/svg/history-dismiss-icon.svg?raw';
+
+import './chat-action-button.js';
+
+export class ChatHistoryController implements ReactiveController {
+ host: ReactiveControllerHost;
+ static CHATHISTORY_ID = 'component:chat-history';
+
+ chatHistory: ChatThreadEntry[] = [];
+
+ private _showChatHistory: boolean = false;
+
+ get showChatHistory() {
+ return this._showChatHistory;
+ }
+
+ set showChatHistory(value: boolean) {
+ this._showChatHistory = value;
+ this.host.requestUpdate();
+ }
+
+ constructor(host: ReactiveControllerHost) {
+ (this.host = host).addController(this);
+ }
+
+ hostConnected() {
+ const chatHistory = localStorage.getItem(ChatHistoryController.CHATHISTORY_ID);
+ if (chatHistory) {
+ // decode base64 string and then parse it
+ const history = JSON.parse(atob(chatHistory));
+
+ // find last 5 user messages indexes
+ const lastUserMessagesIndexes = history
+ .map((entry, index) => {
+ if (entry.isUserMessage) {
+ return index;
+ }
+ })
+ .filter((index) => index !== undefined)
+ .slice(-MAX_CHAT_HISTORY);
+
+ // trim everything before the first user message
+ const trimmedHistory = lastUserMessagesIndexes.length === 0 ? history : history.slice(lastUserMessagesIndexes[0]);
+
+ this.chatHistory = trimmedHistory;
+ }
+ }
+
+ hostDisconnected() {
+ // no-op
+ }
+
+ saveChatHistory(currentChat: ChatThreadEntry[]): void {
+ const newChatHistory = [...this.chatHistory, ...currentChat];
+ // encode to base64 string and then save it
+ localStorage.setItem(ChatHistoryController.CHATHISTORY_ID, btoa(JSON.stringify(newChatHistory)));
+ }
+
+ handleChatHistoryButtonClick(event: Event) {
+ event.preventDefault();
+ this.showChatHistory = !this.showChatHistory;
+ }
+
+ renderHistoryButton(options: { disabled: boolean } | undefined) {
+ return html`
+
${unsafeSVG(iconQuestion)}
`;
diff --git a/packages/chat-component/src/config/global-config.js b/packages/chat-component/src/config/global-config.js
index 32178e4c..017aa5ce 100644
--- a/packages/chat-component/src/config/global-config.js
+++ b/packages/chat-component/src/config/global-config.js
@@ -34,6 +34,10 @@ const globalConfig = {
SUPPORT_CONTEXT_LABEL: 'Support Context',
CITATIONS_LABEL: 'Learn More:',
CITATIONS_TAB_LABEL: 'Citations',
+ SHOW_CHAT_HISTORY_LABEL: 'Show Chat History',
+ HIDE_CHAT_HISTORY_LABEL: 'Hide Chat History',
+ CHAT_MAX_COUNT_TAG: '{MAX_CHAT_HISTORY}',
+ CHAT_HISTORY_FOOTER_TEXT: 'Showing past {MAX_CHAT_HISTORY} conversations',
};
const teaserListTexts = {
@@ -72,6 +76,16 @@ const chatHttpOptions = {
stream: true,
};
+const MAX_CHAT_HISTORY = 5;
+
const APPROACH_MODEL = ['rrr', 'rtr'];
-export { globalConfig, requestOptions, chatHttpOptions, NEXT_QUESTION_INDICATOR, APPROACH_MODEL, teaserListTexts };
+export {
+ globalConfig,
+ requestOptions,
+ chatHttpOptions,
+ NEXT_QUESTION_INDICATOR,
+ APPROACH_MODEL,
+ teaserListTexts,
+ MAX_CHAT_HISTORY,
+};
diff --git a/packages/chat-component/src/core/http/index.ts b/packages/chat-component/src/core/http/index.ts
index cb4b1db4..fe8d8831 100644
--- a/packages/chat-component/src/core/http/index.ts
+++ b/packages/chat-component/src/core/http/index.ts
@@ -1,7 +1,7 @@
import { ChatResponseError } from '../../utils/index.js';
export async function callHttpApi(
- { question, type, approach, overrides }: ChatRequestOptions,
+ { question, type, approach, overrides, messages }: ChatRequestOptions,
{ method, url, stream, signal }: ChatHttpOptions,
) {
return await fetch(`${url}/${type}`, {
@@ -12,6 +12,7 @@ export async function callHttpApi(
signal,
body: JSON.stringify({
messages: [
+ ...(messages ?? []),
{
content: question,
role: 'user',
diff --git a/packages/chat-component/src/styles/chat-action-button.ts b/packages/chat-component/src/styles/chat-action-button.ts
index 4c3c6e37..12c883fe 100644
--- a/packages/chat-component/src/styles/chat-action-button.ts
+++ b/packages/chat-component/src/styles/chat-action-button.ts
@@ -50,6 +50,7 @@ export const styles = css`
svg {
fill: currentColor;
width: 20px;
+ height: 20px;
padding: 3px;
}
button:hover > span,
diff --git a/packages/chat-component/src/styles/chat-component.ts b/packages/chat-component/src/styles/chat-component.ts
index 28b0f577..c360b22e 100644
--- a/packages/chat-component/src/styles/chat-component.ts
+++ b/packages/chat-component/src/styles/chat-component.ts
@@ -260,4 +260,18 @@ export const chatStyle = css`
border-radius: 25px;
padding: 20px;
}
+ .chat-history__footer {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ justify-content: space-between;
+ align-self: center;
+ padding: 20px;
+ }
+ .chat-history__container {
+ display: flex;
+ flex-direction: column;
+ border-bottom: 3px solid var(--light-gray);
+ margin-bottom: 30px;
+ }
`;
diff --git a/packages/chat-component/src/types.d.ts b/packages/chat-component/src/types.d.ts
index bb9147da..3aa5ceb2 100644
--- a/packages/chat-component/src/types.d.ts
+++ b/packages/chat-component/src/types.d.ts
@@ -40,6 +40,7 @@ declare interface ChatRequestOptions {
overrides: RequestOverrides;
type: string;
question: string;
+ messages?: Message[];
}
declare interface RequestOverrides {
diff --git a/packages/chat-component/src/utils/index.ts b/packages/chat-component/src/utils/index.ts
index 1f53bb3a..015add23 100644
--- a/packages/chat-component/src/utils/index.ts
+++ b/packages/chat-component/src/utils/index.ts
@@ -77,6 +77,15 @@ export function getTimestamp() {
});
}
+export function chatEntryToString(entry: ChatThreadEntry) {
+ const message = entry.text
+ .map((textEntry) => textEntry.value + '\n\n' + textEntry.followingSteps?.map((s, i) => `${i + 1}.` + s).join('\n'))
+ .join('\n\n')
+ .replaceAll(/