-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Security Solution][Detection Engine] adds AI Assistant to rule creat…
…e form (#179091) ## Summary - adds AI assistant for queries for every rule type, apart Machine Learning - AI assistant is shown only when query is not empty and invalid - When user clicks on assistant it records telemetry event `open_assistant_on_rule_query_error ` - hidden behind `AIAssistantOnRuleCreationFormEnabled` feature flag ## Design [Design](https://www.figma.com/file/nbgUduTmTpYNXLf1vDMP7u/General-Enhancements?type=design&node-id=115%3A5166&mode=design&t=2Yi5wvS1aDoYxuyT-1) ![AI assistant help contextual](https://github.com/elastic/kibana/assets/119798995/c2ad0989-dd1a-4082-be83-bed7741131be) ## Demo https://github.com/elastic/kibana/assets/92328789/92435f3b-c51e-471b-940f-604a1f245e94 ## Old Demoes **Note: old demo videos use old UI design, and assistant is shown even for valid queries.** <details> <summary>list of videos</summary> ### ES|QL Case 1 Simple ES|QL query validation error solving There 2 problems in query highlighted by validation. First, missing metadata operator Second, operator `=` instead of `==` By feeding query twice in Ai Assistant, I was able to get working solution https://github.com/elastic/kibana/assets/92328789/1eb49505-b161-4fdb-ac3c-d2833c16e2cd ### ES|QL Case 2 Fixes missing _id field, when metadata operator is present https://github.com/elastic/kibana/assets/92328789/82024fcb-822e-46f1-a80a-8b9f1725816e ### EQL Case 1 fixes EQL typo https://github.com/elastic/kibana/assets/92328789/ea18ceec-92f8-4322-b359-50e689a0ef72 </details> ### Issues Results might not be always consistent and for more complex queries they might not correct https://github.com/elastic/kibana/assets/92328789/e3bedfd6-943c-4979-8708-f6c33d1756a6 --------- Co-authored-by: Kibana Machine <[email protected]>
- Loading branch information
1 parent
a6cc252
commit 3506e14
Showing
14 changed files
with
334 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
..._solution/public/detection_engine/rule_creation_ui/components/ai_assistant/index.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import React from 'react'; | ||
import { screen, render } from '@testing-library/react'; | ||
|
||
import { TestProviders } from '../../../../common/mock'; | ||
import { useAssistantAvailability } from '../../../../assistant/use_assistant_availability'; | ||
|
||
import { AiAssistant } from '.'; | ||
|
||
jest.mock('../../../../assistant/use_assistant_availability', () => ({ | ||
useAssistantAvailability: jest.fn(), | ||
})); | ||
|
||
const useAssistantAvailabilityMock = useAssistantAvailability as jest.Mock; | ||
|
||
describe('AiAssistant', () => { | ||
beforeEach(() => { | ||
useAssistantAvailabilityMock.mockReturnValue({ hasAssistantPrivilege: true }); | ||
}); | ||
it('does not render chat component when does not have hasAssistantPrivilege', () => { | ||
useAssistantAvailabilityMock.mockReturnValue({ hasAssistantPrivilege: false }); | ||
|
||
const { container } = render(<AiAssistant getFields={jest.fn()} />, { | ||
wrapper: TestProviders, | ||
}); | ||
|
||
expect(container).toBeEmptyDOMElement(); | ||
}); | ||
it('renders chat component when has hasAssistantPrivilege', () => { | ||
render(<AiAssistant getFields={jest.fn()} />, { | ||
wrapper: TestProviders, | ||
}); | ||
|
||
expect(screen.getByTestId('newChatLink')).toBeInTheDocument(); | ||
}); | ||
}); |
106 changes: 106 additions & 0 deletions
106
...urity_solution/public/detection_engine/rule_creation_ui/components/ai_assistant/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import React, { useCallback } from 'react'; | ||
import { EuiSpacer } from '@elastic/eui'; | ||
import { FormattedMessage } from '@kbn/i18n-react'; | ||
|
||
import { NewChat, AssistantAvatar } from '@kbn/elastic-assistant'; | ||
|
||
import { METRIC_TYPE, TELEMETRY_EVENT, track } from '../../../../common/lib/telemetry'; | ||
import { useAssistantAvailability } from '../../../../assistant/use_assistant_availability'; | ||
import * as i18nAssistant from '../../../../detections/pages/detection_engine/rules/translations'; | ||
import type { DefineStepRule } from '../../../../detections/pages/detection_engine/rules/types'; | ||
import type { FormHook, ValidationError } from '../../../../shared_imports'; | ||
|
||
import * as i18n from './translations'; | ||
|
||
const getLanguageName = (language: string | undefined) => { | ||
let modifiedLanguage = language; | ||
if (language === 'eql') { | ||
modifiedLanguage = 'EQL(Event Query Language)'; | ||
} | ||
if (language === 'esql') { | ||
modifiedLanguage = 'ES|QL(The Elasticsearch Query Language)'; | ||
} | ||
|
||
return modifiedLanguage; | ||
}; | ||
|
||
const retrieveErrorMessages = (errors: ValidationError[]): string => | ||
errors | ||
.flatMap(({ message, messages }) => [message, ...(Array.isArray(messages) ? messages : [])]) | ||
.join(', '); | ||
|
||
interface AiAssistantProps { | ||
getFields: FormHook<DefineStepRule>['getFields']; | ||
language?: string | undefined; | ||
} | ||
|
||
const AiAssistantComponent: React.FC<AiAssistantProps> = ({ getFields, language }) => { | ||
const { hasAssistantPrivilege, isAssistantEnabled } = useAssistantAvailability(); | ||
|
||
const languageName = getLanguageName(language); | ||
|
||
const getPromptContext = useCallback(async () => { | ||
const queryField = getFields().queryBar; | ||
const { query } = (queryField.value as DefineStepRule['queryBar']).query; | ||
|
||
if (!query) { | ||
return ''; | ||
} | ||
|
||
if (queryField.errors.length === 0) { | ||
return `No errors in ${languageName} language query detected. Current query: ${query.trim()}`; | ||
} | ||
|
||
return `${languageName} language query written for Elastic Security Detection rules: \"${query.trim()}\" | ||
returns validation error on form: \"${retrieveErrorMessages(queryField.errors)}\" | ||
Fix ${languageName} language query and give an example of it in markdown format that can be copied. | ||
Proposed solution should be valid and must not contain new line symbols (\\n)`; | ||
}, [getFields, languageName]); | ||
|
||
const onShowOverlay = useCallback(() => { | ||
track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.OPEN_ASSISTANT_ON_RULE_QUERY_ERROR); | ||
}, []); | ||
|
||
if (!hasAssistantPrivilege) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<> | ||
<EuiSpacer size="s" /> | ||
|
||
<FormattedMessage | ||
id="xpack.securitySolution.detectionEngine.createRule.stepDefineRule.askAssistantHelpText" | ||
defaultMessage="{AiAssistantNewChatLink} to help resolve this error." | ||
values={{ | ||
AiAssistantNewChatLink: ( | ||
<NewChat | ||
asLink={true} | ||
category="detection-rules" | ||
conversationId={i18nAssistant.DETECTION_RULES_CONVERSATION_ID} | ||
description={i18n.ASK_ASSISTANT_DESCRIPTION} | ||
getPromptContext={getPromptContext} | ||
suggestedUserPrompt={i18n.ASK_ASSISTANT_USER_PROMPT(languageName)} | ||
tooltip={i18n.ASK_ASSISTANT_TOOLTIP} | ||
iconType={null} | ||
onShowOverlay={onShowOverlay} | ||
isAssistantEnabled={isAssistantEnabled} | ||
> | ||
<AssistantAvatar size="xxs" /> {i18n.ASK_ASSISTANT_ERROR_BUTTON} | ||
</NewChat> | ||
), | ||
}} | ||
/> | ||
</> | ||
); | ||
}; | ||
|
||
export const AiAssistant = React.memo(AiAssistantComponent); | ||
AiAssistant.displayName = 'AiAssistant'; |
39 changes: 39 additions & 0 deletions
39
...solution/public/detection_engine/rule_creation_ui/components/ai_assistant/translations.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { i18n } from '@kbn/i18n'; | ||
|
||
export const ASK_ASSISTANT_ERROR_BUTTON = i18n.translate( | ||
'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.askAssistant', | ||
{ | ||
defaultMessage: 'Ask Assistant', | ||
} | ||
); | ||
|
||
export const ASK_ASSISTANT_DESCRIPTION = i18n.translate( | ||
'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.askAssistantDesc', | ||
{ | ||
defaultMessage: 'Rule query error', | ||
} | ||
); | ||
|
||
export const ASK_ASSISTANT_USER_PROMPT = (language: string | undefined) => | ||
i18n.translate( | ||
'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.askAssistantUserPrompt', | ||
{ | ||
defaultMessage: | ||
'Explain all the errors present in the {language} query above. Generate a new working query, making sure all the errors are resolved in your response.', | ||
values: { language }, | ||
} | ||
); | ||
|
||
export const ASK_ASSISTANT_TOOLTIP = i18n.translate( | ||
'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.askAssistantToolTip', | ||
{ | ||
defaultMessage: 'Fix query or generate new one', | ||
} | ||
); |
Oops, something went wrong.