Skip to content

Commit a4ae90d

Browse files
gagiklerouxb
andauthored
chore(compass-assistant): hide clear chat button when messages are empty COMPASS-9788 (#7379)
* chore(compass-assistant): hide clear chat button when messages are empty COMPASS-9788 * chore: hide aria-label * fix: use app name for request origin * use the correct / agreed upon X-Request-Origin header --------- Co-authored-by: Le Roux Bodenstein <[email protected]> Co-authored-by: Le Roux Bodenstein <[email protected]>
1 parent 91f44a8 commit a4ae90d

File tree

7 files changed

+134
-39
lines changed

7 files changed

+134
-39
lines changed

packages/compass-assistant/src/compass-assistant-drawer.tsx

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,20 @@ import {
77
IconButton,
88
showConfirmation,
99
spacing,
10+
Tooltip,
1011
} from '@mongodb-js/compass-components';
1112
import { AssistantChat } from './components/assistant-chat';
1213
import {
1314
ASSISTANT_DRAWER_ID,
14-
AssistantActionsContext,
1515
AssistantContext,
16+
type AssistantMessage,
1617
} from './compass-assistant-provider';
1718
import {
1819
useIsAIFeatureEnabled,
1920
usePreference,
2021
} from 'compass-preferences-model/provider';
22+
import { useChat } from './@ai-sdk/react/use-chat';
23+
import type { Chat } from './@ai-sdk/react/chat-react';
2124

2225
const assistantTitleStyles = css({
2326
display: 'flex',
@@ -54,25 +57,10 @@ export const CompassAssistantDrawer: React.FunctionComponent<{
5457
hasNonGenuineConnections?: boolean;
5558
}> = ({ appName, autoOpen, hasNonGenuineConnections = false }) => {
5659
const chat = useContext(AssistantContext);
57-
const { clearChat } = useContext(AssistantActionsContext);
5860

5961
const enableAIAssistant = usePreference('enableAIAssistant');
6062
const isAiFeatureEnabled = useIsAIFeatureEnabled();
6163

62-
const handleClearChat = useCallback(async () => {
63-
const confirmed = await showConfirmation({
64-
title: 'Clear this chat?',
65-
description:
66-
'The current chat will be cleared, and chat history will not be retrievable.',
67-
buttonText: 'Clear chat',
68-
variant: 'danger',
69-
'data-testid': 'assistant-confirm-clear-chat-modal',
70-
});
71-
if (confirmed) {
72-
await clearChat?.();
73-
}
74-
}, [clearChat]);
75-
7664
if (!enableAIAssistant || !isAiFeatureEnabled) {
7765
return null;
7866
}
@@ -92,16 +80,7 @@ export const CompassAssistantDrawer: React.FunctionComponent<{
9280
<span className={assistantTitleTextStyles}>MongoDB Assistant</span>
9381
<Badge variant="blue">Preview</Badge>
9482
</div>
95-
<IconButton
96-
aria-label="Clear chat"
97-
onClick={() => {
98-
void handleClearChat();
99-
}}
100-
title="Clear chat"
101-
data-testid="assistant-clear-chat"
102-
>
103-
<Icon glyph="Eraser" />
104-
</IconButton>
83+
<ClearChatButton chat={chat} />
10584
</div>
10685
}
10786
label="MongoDB Assistant"
@@ -123,3 +102,55 @@ export const CompassAssistantDrawer: React.FunctionComponent<{
123102
</DrawerSection>
124103
);
125104
};
105+
106+
export const ClearChatButton: React.FunctionComponent<{
107+
chat: Chat<AssistantMessage>;
108+
}> = ({ chat }) => {
109+
const { clearError, stop } = useChat({ chat });
110+
111+
const handleClearChat = useCallback(async () => {
112+
const confirmed = await showConfirmation({
113+
title: 'Clear this chat?',
114+
description:
115+
'The current chat will be cleared, and chat history will not be retrievable.',
116+
buttonText: 'Clear chat',
117+
variant: 'danger',
118+
'data-testid': 'assistant-confirm-clear-chat-modal',
119+
});
120+
if (confirmed) {
121+
await stop();
122+
clearError();
123+
chat.messages = chat.messages.filter(
124+
(message) => message.metadata?.isPermanent
125+
);
126+
}
127+
}, [stop, clearError, chat]);
128+
129+
const isChatEmpty =
130+
chat.messages.filter((message) => !message.metadata?.isPermanent).length ===
131+
0;
132+
133+
if (isChatEmpty) {
134+
return null;
135+
}
136+
137+
return (
138+
<Tooltip
139+
trigger={
140+
<IconButton
141+
onClick={() => {
142+
void handleClearChat();
143+
}}
144+
title="Clear chat"
145+
aria-label="Clear chat"
146+
aria-hidden={true}
147+
data-testid="assistant-clear-chat"
148+
>
149+
<Icon glyph="Eraser" />
150+
</IconButton>
151+
}
152+
>
153+
Clear chat
154+
</Tooltip>
155+
);
156+
};

packages/compass-assistant/src/compass-assistant-provider.spec.tsx

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,11 @@ const TestComponent: React.FunctionComponent<{
8585

8686
return (
8787
<DrawerContentProvider>
88-
<MockedProvider appNameForPrompt="MongoDB Compass" chat={chat}>
88+
<MockedProvider
89+
originForPrompt="mongodb-compass"
90+
appNameForPrompt="MongoDB Compass"
91+
chat={chat}
92+
>
8993
<DrawerAnchor>
9094
<div data-testid="provider-children">Provider children</div>
9195
<CompassAssistantDrawer
@@ -106,7 +110,11 @@ describe('useAssistantActions', function () {
106110

107111
return (
108112
<DrawerContentProvider>
109-
<MockedProvider appNameForPrompt="MongoDB Compass" chat={chat}>
113+
<MockedProvider
114+
originForPrompt="mongodb-compass"
115+
appNameForPrompt="MongoDB Compass"
116+
chat={chat}
117+
>
110118
{children}
111119
</MockedProvider>
112120
</DrawerContentProvider>
@@ -485,6 +493,56 @@ describe('CompassAssistantProvider', function () {
485493
});
486494

487495
describe('clear chat button', function () {
496+
it('is hidden when the chat is empty', async function () {
497+
const mockChat = createMockChat({ messages: [] });
498+
await renderOpenAssistantDrawer({ chat: mockChat });
499+
expect(screen.queryByTestId('assistant-clear-chat')).to.not.exist;
500+
});
501+
502+
it('is hidden when the chat has only permanent messages', async function () {
503+
const mockChat = createMockChat({
504+
messages: mockMessages.map((message) => ({
505+
...message,
506+
metadata: { isPermanent: true },
507+
})),
508+
});
509+
await renderOpenAssistantDrawer({ chat: mockChat });
510+
expect(screen.queryByTestId('assistant-clear-chat')).to.not.exist;
511+
});
512+
513+
it('is visible when the chat has messages', async function () {
514+
const mockChat = createMockChat({ messages: mockMessages });
515+
await renderOpenAssistantDrawer({ chat: mockChat });
516+
expect(screen.getByTestId('assistant-clear-chat')).to.exist;
517+
});
518+
519+
it('appears after a message is sent', async function () {
520+
const mockChat = new Chat<AssistantMessage>({
521+
messages: [],
522+
transport: {
523+
sendMessages: sinon.stub().returns(
524+
new Promise(() => {
525+
return new ReadableStream({});
526+
})
527+
),
528+
reconnectToStream: sinon.stub(),
529+
},
530+
});
531+
await renderOpenAssistantDrawer({ chat: mockChat });
532+
533+
expect(screen.queryByTestId('assistant-clear-chat')).to.not.exist;
534+
535+
userEvent.type(
536+
screen.getByPlaceholderText('Ask a question'),
537+
'Hello assistant'
538+
);
539+
userEvent.click(screen.getByLabelText('Send message'));
540+
541+
await waitFor(() => {
542+
expect(screen.getByTestId('assistant-clear-chat')).to.exist;
543+
});
544+
});
545+
488546
it('clears the chat when the user clicks and confirms', async function () {
489547
const mockChat = createMockChat({ messages: mockMessages });
490548

@@ -609,7 +667,10 @@ describe('CompassAssistantProvider', function () {
609667
render(
610668
<DrawerContentProvider>
611669
<DrawerAnchor />
612-
<MockedProvider appNameForPrompt="MongoDB Compass" />
670+
<MockedProvider
671+
originForPrompt="mongodb-compass"
672+
appNameForPrompt="MongoDB Compass"
673+
/>
613674
</DrawerContentProvider>,
614675
{
615676
preferences: {

packages/compass-assistant/src/compass-assistant-provider.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ type AssistantActionsContextType = {
7777
connectionInfo: ConnectionInfo;
7878
error: Error;
7979
}) => void;
80-
clearChat?: () => Promise<void>;
8180
tellMoreAboutInsight?: (context: ProactiveInsightsContext) => void;
8281
ensureOptInAndSend?: (
8382
message: SendMessage,
@@ -88,7 +87,7 @@ type AssistantActionsContextType = {
8887

8988
type AssistantActionsType = Omit<
9089
AssistantActionsContextType,
91-
'ensureOptInAndSend' | 'clearChat'
90+
'ensureOptInAndSend'
9291
> & {
9392
getIsAssistantEnabled: () => boolean;
9493
};
@@ -98,7 +97,6 @@ export const AssistantActionsContext =
9897
interpretExplainPlan: () => {},
9998
interpretConnectionError: () => {},
10099
tellMoreAboutInsight: () => {},
101-
clearChat: async () => {},
102100
ensureOptInAndSend: async () => {},
103101
});
104102

@@ -215,13 +213,6 @@ export const AssistantProvider: React.FunctionComponent<
215213
'performance insights',
216214
buildProactiveInsightsPrompt
217215
),
218-
clearChat: async () => {
219-
await chat.stop();
220-
chat.clearError();
221-
chat.messages = chat.messages.filter(
222-
(message) => message.metadata?.isPermanent
223-
);
224-
},
225216
ensureOptInAndSend: async (
226217
message: SendMessage,
227218
options: SendOptions,
@@ -265,6 +256,7 @@ export const CompassAssistantProvider = registerCompassPlugin(
265256
children,
266257
}: PropsWithChildren<{
267258
appNameForPrompt: string;
259+
originForPrompt: string;
268260
chat?: Chat<AssistantMessage>;
269261
atlasAiService?: AtlasAiService;
270262
}>) => {
@@ -289,6 +281,7 @@ export const CompassAssistantProvider = registerCompassPlugin(
289281
initialProps.chat ??
290282
new Chat({
291283
transport: new DocsProviderTransport({
284+
origin: initialProps.originForPrompt,
292285
instructions: buildConversationInstructionsPrompt({
293286
target: initialProps.appNameForPrompt,
294287
}),

packages/compass-assistant/src/docs-provider-transport.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ describe('DocsProviderTransport', function () {
6262
});
6363
abortController = new AbortController();
6464
transport = new DocsProviderTransport({
65+
origin: 'mongodb-compass',
6566
instructions: 'Test instructions for MongoDB assistance',
6667
model: mockModel,
6768
});

packages/compass-assistant/src/docs-provider-transport.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,21 @@ export function shouldExcludeMessage({ metadata }: AssistantMessage) {
1717

1818
export class DocsProviderTransport implements ChatTransport<AssistantMessage> {
1919
private model: LanguageModel;
20+
private origin: string;
2021
private instructions: string;
2122

2223
constructor({
2324
instructions,
2425
model,
26+
origin,
2527
}: {
2628
instructions: string;
2729
model: LanguageModel;
30+
origin: string;
2831
}) {
2932
this.instructions = instructions;
3033
this.model = model;
34+
this.origin = origin;
3135
}
3236

3337
static emptyStream = new ReadableStream<UIMessageChunk>({
@@ -60,6 +64,9 @@ export class DocsProviderTransport implements ChatTransport<AssistantMessage> {
6064
model: this.model,
6165
messages: convertToModelMessages(filteredMessages),
6266
abortSignal: abortSignal,
67+
headers: {
68+
'X-Request-Origin': this.origin,
69+
},
6370
providerOptions: {
6471
openai: {
6572
instructions: this.instructions,

packages/compass-web/src/entrypoint.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ const CompassWeb = ({
512512
>
513513
<CompassInstanceStorePlugin>
514514
<CompassAssistantProvider
515+
originForPrompt="atlas-data-explorer"
515516
appNameForPrompt={
516517
APP_NAMES_FOR_PROMPT.DataExplorer
517518
}

packages/compass/src/app/components/home.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ function HomeWithConnections({
151151
<ConnectionStorageProvider value={connectionStorage}>
152152
<FileInputBackendProvider createFileInputBackend={createFileInputBackend}>
153153
<CompassAssistantProvider
154+
originForPrompt="mongodb-compass"
154155
appNameForPrompt={APP_NAMES_FOR_PROMPT.Compass}
155156
>
156157
<CompassConnections

0 commit comments

Comments
 (0)