Skip to content

Commit b82ab8a

Browse files
authored
[Security Assistant] Starter prompts (#224981)
1 parent e140d22 commit b82ab8a

File tree

18 files changed

+580
-9
lines changed

18 files changed

+580
-9
lines changed

x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/security_ai_prompts/use_find_prompts.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export interface UseFindPromptsParams {
1919
context: {
2020
isAssistantEnabled: boolean;
2121
httpFetch: HttpHandler;
22-
toasts: IToasts;
22+
toasts?: IToasts;
2323
};
2424
signal?: AbortSignal | undefined;
2525
params: FindSecurityAIPromptsRequestQuery;
@@ -82,7 +82,7 @@ const getPrompts = async ({
8282
query,
8383
}: {
8484
httpFetch: HttpHandler;
85-
toasts: IToasts;
85+
toasts?: IToasts;
8686
signal?: AbortSignal | undefined;
8787
query: FindSecurityAIPromptsRequestQuery;
8888
}) => {

x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_body/empty_convo.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,42 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui';
1010
import { css } from '@emotion/react';
1111
import { PromptResponse } from '@kbn/elastic-assistant-common';
1212
import { AssistantBeacon } from '@kbn/ai-assistant-icon';
13+
import { useAssistantContext } from '../../..';
14+
import { StarterPrompts } from './starter_prompts';
1315
import { SystemPrompt } from '../prompt_editor/system_prompt';
1416
import { SetupKnowledgeBaseButton } from '../../knowledge_base/setup_knowledge_base_button';
1517
import * as i18n from '../translations';
1618

1719
interface Props {
20+
connectorId?: string;
1821
currentSystemPromptId: string | undefined;
1922
isSettingsModalVisible: boolean;
2023
setIsSettingsModalVisible: Dispatch<SetStateAction<boolean>>;
2124
setCurrentSystemPromptId: (promptId: string | undefined) => void;
2225
allSystemPrompts: PromptResponse[];
26+
setUserPrompt: React.Dispatch<React.SetStateAction<string | null>>;
2327
}
28+
const starterPromptWrapperClassName = css`
29+
max-width: 95%;
30+
`;
2431

2532
export const EmptyConvo: React.FC<Props> = ({
2633
allSystemPrompts,
34+
connectorId,
2735
currentSystemPromptId,
2836
isSettingsModalVisible,
2937
setCurrentSystemPromptId,
3038
setIsSettingsModalVisible,
39+
setUserPrompt,
3140
}) => {
41+
const { assistantAvailability } = useAssistantContext();
3242
return (
33-
<EuiFlexGroup alignItems="center" justifyContent="center" data-test-subj="emptyConvo">
43+
<EuiFlexGroup
44+
alignItems="center"
45+
justifyContent="spaceBetween"
46+
data-test-subj="emptyConvo"
47+
direction="column"
48+
>
3449
<EuiFlexItem grow={false}>
3550
<EuiPanel
3651
hasShadow={false}
@@ -64,6 +79,11 @@ export const EmptyConvo: React.FC<Props> = ({
6479
</EuiFlexGroup>
6580
</EuiPanel>
6681
</EuiFlexItem>
82+
{assistantAvailability.isStarterPromptsEnabled && (
83+
<EuiFlexItem grow={false} css={starterPromptWrapperClassName}>
84+
<StarterPrompts connectorId={connectorId} setUserPrompt={setUserPrompt} />
85+
</EuiFlexItem>
86+
)}
6787
</EuiFlexGroup>
6888
);
6989
};

x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_body/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ interface Props {
4343
http: HttpSetup;
4444
setCurrentSystemPromptId: (promptId: string | undefined) => void;
4545
setIsSettingsModalVisible: Dispatch<SetStateAction<boolean>>;
46+
setUserPrompt: React.Dispatch<React.SetStateAction<string | null>>;
4647
}
4748

4849
export const AssistantBody: FunctionComponent<Props> = ({
@@ -58,6 +59,7 @@ export const AssistantBody: FunctionComponent<Props> = ({
5859
isSettingsModalVisible,
5960
isWelcomeSetup,
6061
setIsSettingsModalVisible,
62+
setUserPrompt,
6163
}) => {
6264
const { euiTheme } = useEuiTheme();
6365

@@ -109,7 +111,7 @@ export const AssistantBody: FunctionComponent<Props> = ({
109111

110112
return (
111113
<EuiFlexGroup direction="column" justifyContent="spaceBetween">
112-
<EuiFlexItem grow={false}>
114+
<EuiFlexItem>
113115
{isLoading ? (
114116
<EuiEmptyPrompt
115117
data-test-subj="animatedLogo"
@@ -127,6 +129,8 @@ export const AssistantBody: FunctionComponent<Props> = ({
127129
isSettingsModalVisible={isSettingsModalVisible}
128130
setCurrentSystemPromptId={setCurrentSystemPromptId}
129131
setIsSettingsModalVisible={setIsSettingsModalVisible}
132+
setUserPrompt={setUserPrompt}
133+
connectorId={currentConversation?.apiConfig?.connectorId}
130134
/>
131135
) : (
132136
<EuiPanel
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import {
9+
formatPromptGroups,
10+
getAllPromptIds,
11+
promptGroups,
12+
StarterPrompts,
13+
} from './starter_prompts';
14+
import { fireEvent, render } from '@testing-library/react';
15+
import { TestProviders } from '../../mock/test_providers/test_providers';
16+
import React from 'react';
17+
import { useFindPrompts } from '../../..';
18+
const mockResponse = [
19+
{
20+
promptId: 'starterPromptTitle1',
21+
prompt: 'starterPromptTitle1 from API yall',
22+
},
23+
{
24+
promptId: 'starterPromptDescription1',
25+
prompt: 'starterPromptDescription1 from API yall',
26+
},
27+
{
28+
promptId: 'starterPromptIcon1',
29+
prompt: 'starterPromptIcon1 from API yall',
30+
},
31+
{
32+
promptId: 'starterPromptPrompt1',
33+
prompt: 'starterPromptPrompt1 from API yall',
34+
},
35+
{
36+
promptId: 'starterPromptDescription2',
37+
prompt: 'starterPromptDescription2 from API yall',
38+
},
39+
{
40+
promptId: 'starterPromptTitle2',
41+
prompt: 'starterPromptTitle2 from API yall',
42+
},
43+
{
44+
promptId: 'starterPromptIcon2',
45+
prompt: 'starterPromptIcon2 from API yall',
46+
},
47+
{
48+
promptId: 'starterPromptPrompt2',
49+
prompt: 'starterPromptPrompt2 from API yall',
50+
},
51+
{
52+
promptId: 'starterPromptDescription3',
53+
prompt: 'starterPromptDescription3 from API yall',
54+
},
55+
{
56+
promptId: 'starterPromptTitle3',
57+
prompt: 'starterPromptTitle3 from API yall',
58+
},
59+
{
60+
promptId: 'starterPromptIcon3',
61+
prompt: 'starterPromptIcon3 from API yall',
62+
},
63+
{
64+
promptId: 'starterPromptPrompt3',
65+
prompt: 'starterPromptPrompt3 from API yall',
66+
},
67+
{
68+
promptId: 'starterPromptDescription4',
69+
prompt: 'starterPromptDescription4 from API yall',
70+
},
71+
{
72+
promptId: 'starterPromptTitle4',
73+
prompt: 'starterPromptTitle4 from API yall',
74+
},
75+
{
76+
promptId: 'starterPromptPrompt4',
77+
prompt: 'starterPromptPrompt4 from API yall',
78+
},
79+
];
80+
81+
const testProps = {
82+
setUserPrompt: jest.fn(),
83+
};
84+
85+
jest.mock('../../..', () => {
86+
return {
87+
useFindPrompts: jest.fn(),
88+
useAssistantContext: () => ({
89+
assistantAvailability: {
90+
isAssistantEnabled: true,
91+
},
92+
http: { fetch: {} },
93+
}),
94+
};
95+
});
96+
97+
describe('StarterPrompts', () => {
98+
it('should return an empty array if no prompts are provided', () => {
99+
expect(getAllPromptIds(promptGroups)).toEqual([
100+
'starterPromptTitle1',
101+
'starterPromptDescription1',
102+
'starterPromptIcon1',
103+
'starterPromptPrompt1',
104+
'starterPromptDescription2',
105+
'starterPromptTitle2',
106+
'starterPromptIcon2',
107+
'starterPromptPrompt2',
108+
'starterPromptDescription3',
109+
'starterPromptTitle3',
110+
'starterPromptIcon3',
111+
'starterPromptPrompt3',
112+
'starterPromptDescription4',
113+
'starterPromptTitle4',
114+
'starterPromptIcon4',
115+
'starterPromptPrompt4',
116+
]);
117+
});
118+
it('should return the correct prompt groups with fetched prompts', () => {
119+
const response = formatPromptGroups(mockResponse);
120+
expect(response).toEqual([
121+
{
122+
description: 'starterPromptDescription1 from API yall',
123+
icon: 'starterPromptIcon1 from API yall',
124+
prompt: 'starterPromptPrompt1 from API yall',
125+
title: 'starterPromptTitle1 from API yall',
126+
},
127+
{
128+
description: 'starterPromptDescription2 from API yall',
129+
icon: 'starterPromptIcon2 from API yall',
130+
prompt: 'starterPromptPrompt2 from API yall',
131+
title: 'starterPromptTitle2 from API yall',
132+
},
133+
{
134+
description: 'starterPromptDescription3 from API yall',
135+
icon: 'starterPromptIcon3 from API yall',
136+
prompt: 'starterPromptPrompt3 from API yall',
137+
title: 'starterPromptTitle3 from API yall',
138+
},
139+
// starterPrompt Group4 should not exist because starterPromptIcon4 is not in the mockResponse
140+
]);
141+
});
142+
it('the component renders correctly with valid props', () => {
143+
(useFindPrompts as jest.Mock).mockReturnValue({ data: { prompts: mockResponse } });
144+
const { getByTestId } = render(
145+
<TestProviders>
146+
<StarterPrompts {...testProps} />
147+
</TestProviders>
148+
);
149+
expect(getByTestId('starterPromptPrompt2 from API yall')).toBeInTheDocument();
150+
});
151+
it('calls setUserPrompt when a prompt is selected', () => {
152+
(useFindPrompts as jest.Mock).mockReturnValue({ data: { prompts: mockResponse } });
153+
const { getByTestId } = render(
154+
<TestProviders>
155+
<StarterPrompts {...testProps} />
156+
</TestProviders>
157+
);
158+
fireEvent.click(getByTestId('starterPromptPrompt2 from API yall'));
159+
expect(testProps.setUserPrompt).toHaveBeenCalledWith('starterPromptPrompt2 from API yall');
160+
});
161+
});

0 commit comments

Comments
 (0)