Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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>
Expand All @@ -112,7 +111,6 @@ export const AssistantTitle: React.FC<{
<ConnectorSelectorInline
isDisabled={isDisabled || selectedConversation === undefined}
onConnectorModalVisibilityChange={() => {}}
onConnectorSelectionChange={() => {}}
selectedConnectorId={selectedConnectorId}
selectedConversation={selectedConversation}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -131,27 +135,33 @@ 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,
[selectedConversation?.apiConfig.provider]
);

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,
},
},
}));
Expand All @@ -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) => {
Expand Down Expand Up @@ -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>
)}
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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]
);
Expand All @@ -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();
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ describe('ConnectorSelectorInline', () => {
<ConnectorSelectorInline
isDisabled={false}
onConnectorModalVisibilityChange={noop}
onConnectorSelectionChange={noop}
selectedConnectorId={undefined}
selectedConversation={undefined}
/>
Expand All @@ -85,7 +84,6 @@ describe('ConnectorSelectorInline', () => {
<ConnectorSelectorInline
isDisabled={false}
onConnectorModalVisibilityChange={noop}
onConnectorSelectionChange={noop}
selectedConnectorId={'missing-connector-id'}
selectedConversation={conversation}
/>
Expand All @@ -105,7 +103,6 @@ describe('ConnectorSelectorInline', () => {
<ConnectorSelectorInline
isDisabled={false}
onConnectorModalVisibilityChange={noop}
onConnectorSelectionChange={noop}
selectedConnectorId={mockConnectors[0].id}
selectedConversation={conversation}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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: (
Expand All @@ -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>
Expand Down Expand Up @@ -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) {
Expand All @@ -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(
Expand Down Expand Up @@ -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();
}}
Expand Down
Loading