[Security Solution] Fixes Preconfigured Connectors not working with Assistant (#164900)

## Summary

Fixes Preconfigured Connectors not working with the Assistant, and also
ensures default `model` from connector will be used first if available
(instead of defaulting to `gpt-3.5-turbo`).

<p align="center">
<img width="500"
src="637f5919-7560-40b0-a8db-681096e77ac0"
/>
</p> 

Note how `Model` is not displayed even though this is an OpenAI
connector:
<p align="center">
<img width="500"
src="2c4bbe91-2851-48d7-8bfe-20e07db52155"
/>
</p> 

Additionally, resolves issue with Detection Rule Assistant CTA not
displaying correctly on some platforms/browsers. It now shows as a
`Chat` button to the right of the table tabs, matching the other
assistant CTA's throughout the application.

<p align="center">
<img width="500"
src="9fcecd54-8e1a-423a-be05-7137632acbc4"
/>
</p> 

And lastly removes `Beta` title from callout since we're going GA in
`8.10` 🎉

<p align="center">
<img width="500"
src="5beb379a-1bc7-4afc-b4bc-09f1d6085211"
/>
</p> 

Resolves:
https://github.com/elastic/kibana/issues/163394#issuecomment-1693431066
Resolves: https://github.com/elastic/kibana/issues/164819


### Checklist

Delete any items that are not applicable to this PR.

- [X] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
This commit is contained in:
Garrett Spong 2023-08-28 14:46:33 -06:00 committed by GitHub
parent 3835392e32
commit 5f9651e0ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 157 additions and 176 deletions

View file

@ -56,7 +56,7 @@ export const AssistantTitle: React.FC<{
const content = useMemo(
() => (
<FormattedMessage
defaultMessage="The Elastic AI Assistant is currently in beta. For more information on the assistant feature and its usage, please reference the {documentationLink}."
defaultMessage="Responses from AI systems may not always be entirely accurate. For more information on the assistant feature and its usage, please reference the {documentationLink}."
id="xpack.elasticAssistant.assistant.technicalPreview.tooltipContent"
values={{
documentationLink,
@ -99,7 +99,6 @@ export const AssistantTitle: React.FC<{
anchorPosition="rightUp"
>
<EuiText data-test-subj="tooltipContent" grow={false} css={{ maxWidth: '400px' }}>
<h4>{i18n.TOOLTIP_TITLE}</h4>
<EuiText size={'s'}>
<p>{content}</p>
</EuiText>
@ -112,7 +111,6 @@ export const AssistantTitle: React.FC<{
<ConnectorSelectorInline
isDisabled={isDisabled || selectedConversation === undefined}
onConnectorModalVisibilityChange={() => {}}
onConnectorSelectionChange={() => {}}
selectedConnectorId={selectedConnectorId}
selectedConversation={selectedConversation}
/>

View file

@ -23,6 +23,8 @@ import { ModelSelector } from '../../../connectorland/models/model_selector/mode
import { UseAssistantContext } from '../../../assistant_context';
import { ConversationSelectorSettings } from '../conversation_selector_settings';
import { getDefaultSystemPrompt } from '../../use_conversation/helpers';
import { useLoadConnectors } from '../../../connectorland/use_load_connectors';
import { getGenAiConfig } from '../../../connectorland/helpers';
export interface ConversationSettingsProps {
actionTypeRegistry: ActionTypeRegistryContract;
@ -63,6 +65,8 @@ export const ConversationSettings: React.FC<ConversationSettingsProps> = React.m
return getDefaultSystemPrompt({ allSystemPrompts, conversation: selectedConversation });
}, [allSystemPrompts, selectedConversation]);
const { data: connectors, isSuccess: areConnectorsFetched } = useLoadConnectors({ http });
// Conversation callbacks
// When top level conversation selection changes
const onConversationSelectionChange = useCallback(
@ -131,10 +135,13 @@ export const ConversationSettings: React.FC<ConversationSettingsProps> = React.m
[selectedConversation, setUpdatedConversationSettings]
);
const selectedConnectorId = useMemo(
() => selectedConversation?.apiConfig.connectorId,
[selectedConversation?.apiConfig.connectorId]
);
const selectedConnector = useMemo(() => {
const selectedConnectorId = selectedConversation?.apiConfig.connectorId;
if (areConnectorsFetched) {
return connectors?.find((c) => c.id === selectedConnectorId);
}
return undefined;
}, [areConnectorsFetched, connectors, selectedConversation?.apiConfig.connectorId]);
const selectedProvider = useMemo(
() => selectedConversation?.apiConfig.provider,
@ -142,16 +149,19 @@ export const ConversationSettings: React.FC<ConversationSettingsProps> = React.m
);
const handleOnConnectorSelectionChange = useCallback(
(connectorId: string, provider: OpenAiProviderType) => {
(connector) => {
if (selectedConversation != null) {
const config = getGenAiConfig(connector);
setUpdatedConversationSettings((prev) => ({
...prev,
[selectedConversation.id]: {
...selectedConversation,
apiConfig: {
...selectedConversation.apiConfig,
connectorId,
provider,
connectorId: connector?.id,
provider: config?.apiProvider,
model: config?.defaultModel,
},
},
}));
@ -160,10 +170,11 @@ export const ConversationSettings: React.FC<ConversationSettingsProps> = React.m
[selectedConversation, setUpdatedConversationSettings]
);
const selectedModel = useMemo(
() => selectedConversation?.apiConfig.model,
[selectedConversation?.apiConfig.model]
);
const selectedModel = useMemo(() => {
const connectorModel = getGenAiConfig(selectedConnector)?.defaultModel;
// Prefer conversation configuration over connector default
return selectedConversation?.apiConfig.model ?? connectorModel;
}, [selectedConnector, selectedConversation?.apiConfig.model]);
const handleOnModelSelectionChange = useCallback(
(model?: string) => {
@ -244,23 +255,24 @@ export const ConversationSettings: React.FC<ConversationSettingsProps> = React.m
isDisabled={selectedConversation == null}
onConnectorModalVisibilityChange={() => {}}
onConnectorSelectionChange={handleOnConnectorSelectionChange}
selectedConnectorId={selectedConnectorId}
selectedConnectorId={selectedConnector?.id}
/>
</EuiFormRow>
{selectedProvider === OpenAiProviderType.OpenAi && (
<EuiFormRow
data-test-subj="model-field"
display="rowCompressed"
label={i18nModel.MODEL_TITLE}
helpText={i18nModel.HELP_LABEL}
>
<ModelSelector
onModelSelectionChange={handleOnModelSelectionChange}
selectedModel={selectedModel}
/>
</EuiFormRow>
)}
{selectedConnector?.isPreconfigured === false &&
selectedProvider === OpenAiProviderType.OpenAi && (
<EuiFormRow
data-test-subj="model-field"
display="rowCompressed"
label={i18nModel.MODEL_TITLE}
helpText={i18nModel.HELP_LABEL}
>
<ModelSelector
onModelSelectionChange={handleOnModelSelectionChange}
selectedModel={selectedModel}
/>
</EuiFormRow>
)}
</>
);
}

View file

@ -41,13 +41,6 @@ export const API_ERROR = i18n.translate('xpack.elasticAssistant.assistant.apiErr
'An error occurred sending your message. If the problem persists, please test the connector configuration.',
});
export const TOOLTIP_TITLE = i18n.translate(
'xpack.elasticAssistant.assistant.technicalPreview.tooltipTitle',
{
defaultMessage: 'Beta',
}
);
export const TOOLTIP_ARIA_LABEL = i18n.translate(
'xpack.elasticAssistant.documentationLinks.ariaLabel',
{

View file

@ -14,31 +14,24 @@ import {
} from '@kbn/triggers-actions-ui-plugin/public';
import { HttpSetup } from '@kbn/core-http-browser';
import { ActionConnectorProps } from '@kbn/triggers-actions-ui-plugin/public/types';
import { ConnectorAddModal } from '@kbn/triggers-actions-ui-plugin/public/common/constants';
import {
GEN_AI_CONNECTOR_ID,
OpenAiProviderType,
} from '@kbn/stack-connectors-plugin/public/common';
import { GEN_AI_CONNECTOR_ID } from '@kbn/stack-connectors-plugin/public/common';
import { useLoadConnectors } from '../use_load_connectors';
import * as i18n from '../translations';
import { useLoadActionTypes } from '../use_load_action_types';
import { useAssistantContext } from '../../assistant_context';
import { getGenAiConfig } from '../helpers';
export const ADD_NEW_CONNECTOR = 'ADD_NEW_CONNECTOR';
interface Props {
actionTypeRegistry: ActionTypeRegistryContract;
http: HttpSetup;
isDisabled?: boolean;
onConnectorSelectionChange: (connectorId: string, provider: OpenAiProviderType) => void;
onConnectorSelectionChange: (connector: ActionConnector | undefined) => void;
selectedConnectorId?: string;
onConnectorModalVisibilityChange?: (isVisible: boolean) => void;
}
interface Config {
apiProvider: string;
}
export const ConnectorSelector: React.FC<Props> = React.memo(
({
actionTypeRegistry,
@ -95,18 +88,19 @@ export const ConnectorSelector: React.FC<Props> = React.memo(
const connectorOptions = useMemo(() => {
return (
connectors?.map((connector) => {
const apiProvider: string | undefined = (
connector as ActionConnectorProps<Config, unknown>
)?.config?.apiProvider;
const apiProvider = getGenAiConfig(connector)?.apiProvider;
const connectorDetails = connector.isPreconfigured
? i18n.PRECONFIGURED_CONNECTOR
: apiProvider;
return {
value: connector.id,
inputDisplay: connector.name,
dropdownDisplay: (
<React.Fragment key={connector.id}>
<strong>{connector.name}</strong>
{apiProvider && (
<EuiText size="s" color="subdued">
<p>{apiProvider}</p>
{connectorDetails && (
<EuiText size="xs" color="subdued">
<p>{connectorDetails}</p>
</EuiText>
)}
</React.Fragment>
@ -138,10 +132,8 @@ export const ConnectorSelector: React.FC<Props> = React.memo(
return;
}
const apiProvider = (
connectors?.find((c) => c.id === connectorId) as ActionConnectorProps<Config, unknown>
)?.config.apiProvider as OpenAiProviderType;
onConnectorSelectionChange(connectorId, apiProvider);
const connector = connectors?.find((c) => c.id === connectorId);
onConnectorSelectionChange(connector);
},
[connectors, onConnectorSelectionChange, onConnectorModalVisibilityChange]
);
@ -162,12 +154,8 @@ export const ConnectorSelector: React.FC<Props> = React.memo(
<ConnectorAddModal
actionType={actionType}
onClose={cleanupAndCloseModal}
postSaveEventHandler={(savedAction: ActionConnector) => {
onConnectorSelectionChange(
savedAction.id,
(savedAction as ActionConnectorProps<Config, unknown>)?.config
.apiProvider as OpenAiProviderType
);
postSaveEventHandler={(connector: ActionConnector) => {
onConnectorSelectionChange(connector);
refetchConnectors?.();
cleanupAndCloseModal();
}}

View file

@ -65,7 +65,6 @@ describe('ConnectorSelectorInline', () => {
<ConnectorSelectorInline
isDisabled={false}
onConnectorModalVisibilityChange={noop}
onConnectorSelectionChange={noop}
selectedConnectorId={undefined}
selectedConversation={undefined}
/>
@ -85,7 +84,6 @@ describe('ConnectorSelectorInline', () => {
<ConnectorSelectorInline
isDisabled={false}
onConnectorModalVisibilityChange={noop}
onConnectorSelectionChange={noop}
selectedConnectorId={'missing-connector-id'}
selectedConversation={conversation}
/>
@ -105,7 +103,6 @@ describe('ConnectorSelectorInline', () => {
<ConnectorSelectorInline
isDisabled={false}
onConnectorModalVisibilityChange={noop}
onConnectorSelectionChange={noop}
selectedConnectorId={mockConnectors[0].id}
selectedConversation={conversation}
/>

View file

@ -10,7 +10,6 @@ import React, { useCallback, useMemo, useState } from 'react';
import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public';
import { ActionConnectorProps } from '@kbn/triggers-actions-ui-plugin/public/types';
import { ConnectorAddModal } from '@kbn/triggers-actions-ui-plugin/public/common/constants';
import {
GEN_AI_CONNECTOR_ID,
@ -23,20 +22,16 @@ import * as i18n from '../translations';
import { useLoadActionTypes } from '../use_load_action_types';
import { useAssistantContext } from '../../assistant_context';
import { useConversation } from '../../assistant/use_conversation';
import { getGenAiConfig } from '../helpers';
export const ADD_NEW_CONNECTOR = 'ADD_NEW_CONNECTOR';
interface Props {
isDisabled?: boolean;
onConnectorSelectionChange: (connectorId: string, provider: OpenAiProviderType) => void;
selectedConnectorId?: string;
selectedConversation?: Conversation;
onConnectorModalVisibilityChange?: (isVisible: boolean) => void;
}
interface Config {
apiProvider: string;
}
const inputContainerClassName = css`
height: 32px;
@ -82,7 +77,6 @@ export const ConnectorSelectorInline: React.FC<Props> = React.memo(
onConnectorModalVisibilityChange,
selectedConnectorId,
selectedConversation,
onConnectorSelectionChange,
}) => {
const [isOpen, setIsOpen] = useState<boolean>(false);
const { actionTypeRegistry, assistantAvailability, http } = useAssistantContext();
@ -136,9 +130,10 @@ export const ConnectorSelectorInline: React.FC<Props> = React.memo(
const connectorOptions = useMemo(() => {
return (
connectors?.map((connector) => {
const apiProvider: string | undefined = (
connector as ActionConnectorProps<Config, unknown>
)?.config?.apiProvider;
const apiProvider = getGenAiConfig(connector)?.apiProvider;
const connectorDetails = connector.isPreconfigured
? i18n.PRECONFIGURED_CONNECTOR
: apiProvider;
return {
value: connector.id,
inputDisplay: (
@ -149,9 +144,9 @@ export const ConnectorSelectorInline: React.FC<Props> = React.memo(
dropdownDisplay: (
<React.Fragment key={connector.id}>
<strong>{connector.name}</strong>
{apiProvider && (
{connectorDetails && (
<EuiText size="xs" color="subdued">
<p>{apiProvider}</p>
<p>{connectorDetails}</p>
</EuiText>
)}
</React.Fragment>
@ -182,7 +177,7 @@ export const ConnectorSelectorInline: React.FC<Props> = React.memo(
const handleOnBlur = useCallback(() => setIsOpen(false), []);
const onChange = useCallback(
(connectorId: string, apiProvider?: OpenAiProviderType) => {
(connectorId: string, apiProvider?: OpenAiProviderType, model?: string) => {
setIsOpen(false);
if (connectorId === ADD_NEW_CONNECTOR) {
@ -191,31 +186,22 @@ export const ConnectorSelectorInline: React.FC<Props> = React.memo(
return;
}
const provider =
apiProvider ??
((connectors?.find((c) => c.id === connectorId) as ActionConnectorProps<Config, unknown>)
?.config.apiProvider as OpenAiProviderType);
const connector = connectors?.find((c) => c.id === connectorId);
const config = getGenAiConfig(connector);
if (selectedConversation != null) {
setApiConfig({
conversationId: selectedConversation.id,
apiConfig: {
...selectedConversation.apiConfig,
connectorId,
provider,
// With the inline component, prefer config args to handle 'new connector' case
provider: apiProvider ?? config?.apiProvider,
model: model ?? config?.defaultModel,
},
});
}
onConnectorSelectionChange(connectorId, provider);
},
[
connectors,
selectedConversation,
onConnectorSelectionChange,
onConnectorModalVisibilityChange,
setApiConfig,
]
[connectors, selectedConversation, onConnectorModalVisibilityChange, setApiConfig]
);
const placeholderComponent = useMemo(
@ -276,11 +262,9 @@ export const ConnectorSelectorInline: React.FC<Props> = React.memo(
<ConnectorAddModal
actionType={actionType}
onClose={cleanupAndCloseModal}
postSaveEventHandler={(savedAction: ActionConnector) => {
const provider = (savedAction as ActionConnectorProps<Config, unknown>)?.config
.apiProvider as OpenAiProviderType;
onChange(savedAction.id, provider);
onConnectorSelectionChange(savedAction.id, provider);
postSaveEventHandler={(connector: ActionConnector) => {
const config = getGenAiConfig(connector);
onChange(connector.id, config?.apiProvider, config?.defaultModel);
refetchConnectors?.();
cleanupAndCloseModal();
}}

View file

@ -14,11 +14,7 @@ import { ConnectorAddModal } from '@kbn/triggers-actions-ui-plugin/public/common
import type { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public';
import { ActionType } from '@kbn/triggers-actions-ui-plugin/public';
import {
GEN_AI_CONNECTOR_ID,
OpenAiProviderType,
} from '@kbn/stack-connectors-plugin/public/common';
import { ActionConnectorProps } from '@kbn/triggers-actions-ui-plugin/public/types';
import { GEN_AI_CONNECTOR_ID } from '@kbn/stack-connectors-plugin/public/common';
import { WELCOME_CONVERSATION } from '../../assistant/use_conversation/sample_conversations';
import { Conversation, Message } from '../../..';
import { useLoadActionTypes } from '../use_load_action_types';
@ -30,6 +26,7 @@ import * as i18n from '../translations';
import { useAssistantContext } from '../../assistant_context';
import { useLoadConnectors } from '../use_load_connectors';
import { AssistantAvatar } from '../../assistant/assistant_avatar/assistant_avatar';
import { getGenAiConfig } from '../helpers';
const ConnectorButtonWrapper = styled.div`
margin-bottom: 10px;
@ -39,10 +36,6 @@ const SkipEuiText = styled(EuiText)`
margin-top: 20px;
`;
interface Config {
apiProvider: string;
}
export interface ConnectorSetupProps {
conversation?: Conversation;
onSetupComplete?: () => void;
@ -223,16 +216,17 @@ export const useConnectorSetup = ({
<ConnectorAddModal
actionType={actionType}
onClose={() => setIsConnectorModalVisible(false)}
postSaveEventHandler={(savedAction: ActionConnector) => {
postSaveEventHandler={(connector: ActionConnector) => {
const config = getGenAiConfig(connector);
// Add connector to all conversations
Object.values(conversations).forEach((c) => {
setApiConfig({
conversationId: c.id,
apiConfig: {
...c.apiConfig,
connectorId: savedAction.id,
provider: (savedAction as ActionConnectorProps<Config, unknown>)?.config
.apiProvider as OpenAiProviderType,
connectorId: connector.id,
provider: config?.apiProvider,
model: config?.defaultModel,
},
});
});

View file

@ -0,0 +1,31 @@
/*
* 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 type { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public';
import { ActionConnectorProps } from '@kbn/triggers-actions-ui-plugin/public/types';
import { OpenAiProviderType } from '@kbn/stack-connectors-plugin/common/gen_ai/constants';
interface GenAiConfig {
apiProvider?: OpenAiProviderType;
apiUrl?: string;
defaultModel?: string;
}
/**
* Returns the GenAiConfig for a given ActionConnector. Note that if the connector is preconfigured,
* the config will be undefined as the connector is neither available nor editable.
*
* TODO: Extract and use separate types from GenAiConfig from '@kbn/stack-connectors-plugin/common/gen_ai/types'
*
* @param connector
*/
export const getGenAiConfig = (connector: ActionConnector | undefined): GenAiConfig | undefined => {
if (!connector?.isPreconfigured) {
return (connector as ActionConnectorProps<GenAiConfig, unknown>)?.config;
}
return undefined;
};

View file

@ -31,6 +31,13 @@ export const WELCOME_SECURITY = i18n.translate(
}
);
export const PRECONFIGURED_CONNECTOR = i18n.translate(
'xpack.elasticAssistant.assistant.connectors.preconfiguredTitle',
{
defaultMessage: 'Preconfigured',
}
);
export const CONNECTOR_SELECTOR_TITLE = i18n.translate(
'xpack.elasticAssistant.assistant.connectors.connectorSelector.ariaLabel',
{

View file

@ -5,12 +5,18 @@
* 2.0.
*/
import React, { useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { NewChat } from '@kbn/elastic-assistant';
import { useUserData } from '../../../../detections/components/user_info';
import { TabNavigation } from '../../../../common/components/navigation/tab_navigation';
import { usePrebuiltRulesStatus } from '../../../rule_management/logic/prebuilt_rules/use_prebuilt_rules_status';
import { useRuleManagementFilters } from '../../../rule_management/logic/use_rule_management_filters';
import * as i18n from './translations';
import { getPromptContextFromDetectionRules } from '../../../../assistant/helpers';
import { useRulesTableContext } from './rules_table/rules_table_context';
import { useAssistantAvailability } from '../../../../assistant/use_assistant_availability';
import * as i18nAssistant from '../../../../detections/pages/detection_engine/rules/translations';
export enum AllRulesTabs {
management = 'management',
@ -71,7 +77,39 @@ export const RulesTableToolbar = React.memo(() => {
[installedTotal, updateTotal, shouldDisplayRuleUpdatesTab]
);
return <TabNavigation navTabs={ruleTabs} />;
// Assistant integration for using selected rules as prompt context
const { hasAssistantPrivilege } = useAssistantAvailability();
const {
state: { rules, selectedRuleIds },
} = useRulesTableContext();
const selectedRules = useMemo(
() => rules.filter((rule) => selectedRuleIds.includes(rule.id)),
[rules, selectedRuleIds]
);
const getPromptContext = useCallback(
async () => getPromptContextFromDetectionRules(selectedRules),
[selectedRules]
);
return (
<EuiFlexGroup justifyContent={'spaceBetween'}>
<EuiFlexItem grow={false}>
<TabNavigation navTabs={ruleTabs} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
{hasAssistantPrivilege && selectedRules.length > 0 && (
<NewChat
category="detection-rules"
conversationId={i18nAssistant.DETECTION_RULES_CONVERSATION_ID}
description={i18nAssistant.RULE_MANAGEMENT_CONTEXT_DESCRIPTION}
getPromptContext={getPromptContext}
suggestedUserPrompt={i18nAssistant.EXPLAIN_THEN_SUMMARIZE_RULE_DETAILS}
tooltip={i18nAssistant.RULE_MANAGEMENT_CONTEXT_TOOLTIP}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
);
});
RulesTableToolbar.displayName = 'RulesTableToolbar';

View file

@ -32,8 +32,8 @@ import { useInvalidateFindRulesQuery } from '../../../rule_management/api/hooks/
import { importRules } from '../../../rule_management/logic';
import { AllRules } from '../../components/rules_table';
import { RulesTableContextProvider } from '../../components/rules_table/rules_table/rules_table_context';
import { SuperHeader } from './super_header';
import { useInvalidateFetchCoverageOverviewQuery } from '../../../rule_management/api/hooks/use_fetch_coverage_overview';
import { HeaderPage } from '../../../../common/components/header_page';
const RulesPageComponent: React.FC = () => {
const [isImportModalVisible, showImportModal, hideImportModal] = useBoolState();
@ -110,7 +110,7 @@ const RulesPageComponent: React.FC = () => {
<RulesTableContextProvider>
<SecuritySolutionPageWrapper>
<SuperHeader>
<HeaderPage title={i18n.PAGE_TITLE}>
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap={true}>
<EuiFlexItem grow={false}>
<AddElasticRulesButton isDisabled={!canUserCRUD || loading} />
@ -149,7 +149,7 @@ const RulesPageComponent: React.FC = () => {
</SecuritySolutionLinkButton>
</EuiFlexItem>
</EuiFlexGroup>
</SuperHeader>
</HeaderPage>
<MaintenanceWindowCallout kibanaServices={kibanaServices} />
<AllRules data-test-subj="all-rules" />
</SecuritySolutionPageWrapper>

View file

@ -1,61 +0,0 @@
/*
* 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 { NewChat } from '@kbn/elastic-assistant';
import React, { useCallback, useMemo } from 'react';
import { useAssistantAvailability } from '../../../../assistant/use_assistant_availability';
import { getPromptContextFromDetectionRules } from '../../../../assistant/helpers';
import { HeaderPage } from '../../../../common/components/header_page';
import { useRulesTableContext } from '../../components/rules_table/rules_table/rules_table_context';
import * as i18n from '../../../../detections/pages/detection_engine/rules/translations';
export const SuperHeader: React.FC<{ children: React.ReactNode }> = React.memo(({ children }) => {
const { hasAssistantPrivilege } = useAssistantAvailability();
const memoizedChildren = useMemo(() => children, [children]);
// Rules state
const {
state: { rules, selectedRuleIds },
} = useRulesTableContext();
const selectedRules = useMemo(
() => rules.filter((rule) => selectedRuleIds.includes(rule.id)),
[rules, selectedRuleIds]
);
const getPromptContext = useCallback(
async () => getPromptContextFromDetectionRules(selectedRules),
[selectedRules]
);
return (
<HeaderPage
title={
<>
{i18n.PAGE_TITLE}{' '}
{hasAssistantPrivilege && selectedRules.length > 0 && (
<NewChat
category="detection-rules"
conversationId={i18n.DETECTION_RULES_CONVERSATION_ID}
description={i18n.RULE_MANAGEMENT_CONTEXT_DESCRIPTION}
getPromptContext={getPromptContext}
iconType={null}
suggestedUserPrompt={i18n.EXPLAIN_THEN_SUMMARIZE_RULE_DETAILS}
tooltip={i18n.RULE_MANAGEMENT_CONTEXT_TOOLTIP}
>
{'🪄✨'}
</NewChat>
)}
</>
}
>
{memoizedChildren}
</HeaderPage>
);
});
SuperHeader.displayName = 'NewChatComponent';