diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/advanced_settings/advanced_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/advanced_settings/advanced_settings.tsx deleted file mode 100644 index 4474973b3ce62..0000000000000 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/advanced_settings/advanced_settings.tsx +++ /dev/null @@ -1,193 +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 React, { useCallback, useMemo, useState } from 'react'; -import { - EuiFormRow, - EuiTitle, - EuiText, - EuiTextColor, - EuiHorizontalRule, - EuiLoadingSpinner, - EuiSpacer, - EuiSwitch, - EuiToolTip, - EuiSwitchEvent, - EuiLink, -} from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n-react'; -import * as i18n from './translations'; -import { useKnowledgeBaseStatus } from '../../../knowledge_base/use_knowledge_base_status/use_knowledge_base_status'; -import { useAssistantContext } from '../../../assistant_context'; -import { useSetupKnowledgeBase } from '../../../knowledge_base/use_setup_knowledge_base/use_setup_knowledge_base'; -import { useDeleteKnowledgeBase } from '../../../knowledge_base/use_delete_knowledge_base/use_delete_knowledge_base'; - -const ESQL_RESOURCE = 'esql'; -interface Props { - onAdvancedSettingsChange?: () => void; -} - -/** - * Advanced Settings -- enable and disable LangChain integration, Knowledge Base, and ESQL KB Documents - */ -export const AdvancedSettings: React.FC = React.memo(({ onAdvancedSettingsChange }) => { - const { http, assistantLangChain } = useAssistantContext(); - const { - data: kbStatus, - isLoading, - isFetching, - } = useKnowledgeBaseStatus({ http, resource: ESQL_RESOURCE }); - const { mutate: setupKB, isLoading: isSettingUpKB } = useSetupKnowledgeBase({ http }); - const { mutate: deleteKB, isLoading: isDeletingUpKB } = useDeleteKnowledgeBase({ http }); - - const [isLangChainEnabled, setIsLangChainEnabled] = useState(assistantLangChain); - const isKnowledgeBaseEnabled = - (kbStatus?.index_exists && kbStatus?.pipeline_exists && kbStatus?.elser_exists) ?? false; - const isESQLEnabled = kbStatus?.esql_exists ?? false; - - const isLoadingKb = isLoading || isFetching || isSettingUpKB || isDeletingUpKB; - const isKnowledgeBaseAvailable = isLangChainEnabled && kbStatus?.elser_exists; - const isESQLAvailable = isLangChainEnabled && isKnowledgeBaseAvailable && isKnowledgeBaseEnabled; - - const onEnableKnowledgeBaseChange = useCallback( - (event: EuiSwitchEvent) => { - if (event.target.checked) { - setupKB(); - } else { - deleteKB(); - } - }, - [deleteKB, setupKB] - ); - - const onEnableESQLChange = useCallback( - (event: EuiSwitchEvent) => { - if (event.target.checked) { - setupKB(ESQL_RESOURCE); - } else { - deleteKB(ESQL_RESOURCE); - } - }, - [deleteKB, setupKB] - ); - - const langchainSwitch = useMemo(() => { - return ( - setIsLangChainEnabled(!isLangChainEnabled)} - showLabel={false} - /> - ); - }, [isLangChainEnabled]); - - const knowledgeBaseSwitch = useMemo(() => { - return isLoadingKb ? ( - - ) : ( - - - - ); - }, [isLoadingKb, isKnowledgeBaseAvailable, isKnowledgeBaseEnabled, onEnableKnowledgeBaseChange]); - - const esqlSwitch = useMemo(() => { - return isLoadingKb ? ( - - ) : ( - - - - ); - }, [isLoadingKb, isESQLAvailable, isESQLEnabled, onEnableESQLChange]); - - return ( - <> - -

{i18n.SETTINGS_TITLE}

-
- - - {i18n.SETTINGS_DESCRIPTION} - - - - - {langchainSwitch} - - - {i18n.LANNGCHAIN_DESCRIPTION} - - - - {knowledgeBaseSwitch} - - - - - {i18n.KNOWLEDGE_BASE_DESCRIPTION_ELSER_LEARN_MORE} - - ), - }} - /> - - - - - {esqlSwitch} - - - {i18n.ESQL_DESCRIPTION} - - - ); -}); - -AdvancedSettings.displayName = 'AdvancedSettings'; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/advanced_settings/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/advanced_settings/translations.ts deleted file mode 100644 index ca849f0a6f7c5..0000000000000 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/advanced_settings/translations.ts +++ /dev/null @@ -1,79 +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 { i18n } from '@kbn/i18n'; - -export const SETTINGS_TITLE = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.advancedSettings.settingsTitle', - { - defaultMessage: 'Advanced Settings', - } -); -export const SETTINGS_DESCRIPTION = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.advancedSettings.settingsDescription', - { - defaultMessage: 'Additional knobs and dials for the Elastic AI Assistant.', - } -); - -export const LANNGCHAIN_LABEL = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.advancedSettings.langChainLabel', - { - defaultMessage: 'Experimental LangChain Integration', - } -); - -export const LANNGCHAIN_DESCRIPTION = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.advancedSettings.langChainDescription', - { - defaultMessage: - 'Enables advanced features and workflows like the Knowledge Base, Functions, Memories, and advanced agent and chain configurations. ', - } -); - -export const KNOWLEDGE_BASE_LABEL = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.advancedSettings.knowledgeBaseLabel', - { - defaultMessage: 'Knowledge Base', - } -); - -export const KNOWLEDGE_BASE_LABEL_TOOLTIP = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.advancedSettings.knowledgeBaseLabelTooltip', - { - defaultMessage: 'Requires ELSER to be configured and started.', - } -); - -export const KNOWLEDGE_BASE_DESCRIPTION_ELSER_LEARN_MORE = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.advancedSettings.knowledgeBaseElserLearnMoreDescription', - { - defaultMessage: 'Learn more.', - } -); - -export const ESQL_LABEL = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.advancedSettings.esqlLabel', - { - defaultMessage: 'ES|QL Knowledge Base Documents', - } -); - -export const ESQL_LABEL_TOOLTIP = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.advancedSettings.esqlTooltip', - { - defaultMessage: 'Requires `Knowledge Base` to be enabled.', - } -); - -export const ESQL_DESCRIPTION = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.advancedSettings.esqlDescription', - { - defaultMessage: - 'Loads ES|QL documentation and language files into the Knowledge Base for use in generating ES|QL queries.', - } -); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx index caf8fa77a9b61..5b357fb6594cc 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx @@ -30,7 +30,7 @@ import { useAssistantContext } from '../../assistant_context'; import { AnonymizationSettings } from '../../data_anonymization/settings/anonymization_settings'; import { QuickPromptSettings } from '../quick_prompts/quick_prompt_settings/quick_prompt_settings'; import { SystemPromptSettings } from '../prompt_editor/system_prompt/system_prompt_modal/system_prompt_settings'; -import { AdvancedSettings } from './advanced_settings/advanced_settings'; +import { KnowledgeBaseSettings } from '../../knowledge_base/knowledge_base_settings/knowledge_base_settings'; import { ConversationSettings } from '../conversations/conversation_settings/conversation_settings'; import { TEST_IDS } from '../constants'; import { useSettingsUpdater } from './use_settings_updater/use_settings_updater'; @@ -45,7 +45,7 @@ export const CONVERSATIONS_TAB = 'CONVERSATION_TAB' as const; export const QUICK_PROMPTS_TAB = 'QUICK_PROMPTS_TAB' as const; export const SYSTEM_PROMPTS_TAB = 'SYSTEM_PROMPTS_TAB' as const; export const ANONYMIZATION_TAB = 'ANONYMIZATION_TAB' as const; -export const ADVANCED_TAB = 'ADVANCED_TAB' as const; +export const KNOWLEDGE_BASE_TAB = 'KNOWLEDGE_BASE_TAB' as const; export const EVALUATION_TAB = 'EVALUATION_TAB' as const; export type SettingsTabs = @@ -53,7 +53,7 @@ export type SettingsTabs = | typeof QUICK_PROMPTS_TAB | typeof SYSTEM_PROMPTS_TAB | typeof ANONYMIZATION_TAB - | typeof ADVANCED_TAB + | typeof KNOWLEDGE_BASE_TAB | typeof EVALUATION_TAB; interface Props { defaultConnectorId?: string; @@ -68,7 +68,7 @@ interface Props { /** * Modal for overall Assistant Settings, including conversation settings, quick prompts, system prompts, - * anonymization, functions (coming soon!), and advanced settings. + * anonymization, knowledge base, and evaluation via the `isModelEvaluationEnabled` feature flag. */ export const AssistantSettings: React.FC = React.memo( ({ @@ -79,17 +79,19 @@ export const AssistantSettings: React.FC = React.memo( selectedConversation: defaultSelectedConversation, setSelectedConversationId, }) => { - const { assistantLangChain, http, selectedSettingsTab, setSelectedSettingsTab } = + const { modelEvaluatorEnabled, http, selectedSettingsTab, setSelectedSettingsTab } = useAssistantContext(); const { conversationSettings, defaultAllow, defaultAllowReplacement, + knowledgeBase, quickPromptSettings, systemPromptSettings, setUpdatedConversationSettings, setUpdatedDefaultAllow, setUpdatedDefaultAllowReplacement, + setUpdatedKnowledgeBaseSettings, setUpdatedQuickPromptSettings, setUpdatedSystemPromptSettings, saveSettings, @@ -236,17 +238,15 @@ export const AssistantSettings: React.FC = React.memo( > - {assistantLangChain && ( - setSelectedSettingsTab(ADVANCED_TAB)} - > - - - )} - {assistantLangChain && ( + setSelectedSettingsTab(KNOWLEDGE_BASE_TAB)} + > + + + {modelEvaluatorEnabled && ( = React.memo( setUpdatedDefaultAllowReplacement={setUpdatedDefaultAllowReplacement} /> )} - {selectedSettingsTab === ADVANCED_TAB && } + {selectedSettingsTab === KNOWLEDGE_BASE_TAB && ( + + )} {selectedSettingsTab === EVALUATION_TAB && } void; systemPromptSettings: Prompt[]; @@ -21,6 +23,7 @@ interface UseSettingsUpdater { setUpdatedConversationSettings: React.Dispatch< React.SetStateAction >; + setUpdatedKnowledgeBaseSettings: React.Dispatch>; setUpdatedQuickPromptSettings: React.Dispatch>; setUpdatedSystemPromptSettings: React.Dispatch>; saveSettings: () => void; @@ -34,11 +37,13 @@ export const useSettingsUpdater = (): UseSettingsUpdater => { conversations, defaultAllow, defaultAllowReplacement, + knowledgeBase, setAllQuickPrompts, setAllSystemPrompts, setConversations, setDefaultAllow, setDefaultAllowReplacement, + setKnowledgeBase, } = useAssistantContext(); /** @@ -57,6 +62,9 @@ export const useSettingsUpdater = (): UseSettingsUpdater => { const [updatedDefaultAllow, setUpdatedDefaultAllow] = useState(defaultAllow); const [updatedDefaultAllowReplacement, setUpdatedDefaultAllowReplacement] = useState(defaultAllowReplacement); + // Knowledge Base + const [updatedKnowledgeBaseSettings, setUpdatedKnowledgeBaseSettings] = + useState(knowledgeBase); /** * Reset all pending settings @@ -64,10 +72,18 @@ export const useSettingsUpdater = (): UseSettingsUpdater => { const resetSettings = useCallback((): void => { setUpdatedConversationSettings(conversations); setUpdatedQuickPromptSettings(allQuickPrompts); + setUpdatedKnowledgeBaseSettings(knowledgeBase); setUpdatedSystemPromptSettings(allSystemPrompts); setUpdatedDefaultAllow(defaultAllow); setUpdatedDefaultAllowReplacement(defaultAllowReplacement); - }, [allQuickPrompts, allSystemPrompts, conversations, defaultAllow, defaultAllowReplacement]); + }, [ + allQuickPrompts, + allSystemPrompts, + conversations, + defaultAllow, + defaultAllowReplacement, + knowledgeBase, + ]); /** * Save all pending settings @@ -76,6 +92,7 @@ export const useSettingsUpdater = (): UseSettingsUpdater => { setAllQuickPrompts(updatedQuickPromptSettings); setAllSystemPrompts(updatedSystemPromptSettings); setConversations(updatedConversationSettings); + setKnowledgeBase(updatedKnowledgeBaseSettings); setDefaultAllow(updatedDefaultAllow); setDefaultAllowReplacement(updatedDefaultAllowReplacement); }, [ @@ -84,9 +101,11 @@ export const useSettingsUpdater = (): UseSettingsUpdater => { setConversations, setDefaultAllow, setDefaultAllowReplacement, + setKnowledgeBase, updatedConversationSettings, updatedDefaultAllow, updatedDefaultAllowReplacement, + updatedKnowledgeBaseSettings, updatedQuickPromptSettings, updatedSystemPromptSettings, ]); @@ -95,6 +114,7 @@ export const useSettingsUpdater = (): UseSettingsUpdater => { conversationSettings: updatedConversationSettings, defaultAllow: updatedDefaultAllow, defaultAllowReplacement: updatedDefaultAllowReplacement, + knowledgeBase: updatedKnowledgeBaseSettings, quickPromptSettings: updatedQuickPromptSettings, resetSettings, systemPromptSettings: updatedSystemPromptSettings, @@ -102,6 +122,7 @@ export const useSettingsUpdater = (): UseSettingsUpdater => { setUpdatedDefaultAllow, setUpdatedDefaultAllowReplacement, setUpdatedConversationSettings, + setUpdatedKnowledgeBaseSettings, setUpdatedQuickPromptSettings, setUpdatedSystemPromptSettings, }; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/types.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/types.ts index dd5d184abfd20..e9ffd8f8014e8 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/types.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/types.ts @@ -15,3 +15,7 @@ export interface Prompt { isDefault?: boolean; // TODO: Should be renamed to isImmutable as this flag is used to prevent users from deleting prompts isNewConversationDefault?: boolean; } + +export interface KnowledgeBaseConfig { + assistantLangChain: boolean; +} diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_messages/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_messages/index.tsx index 38dc60c5fc9e7..f9f63aa8ef8ac 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_messages/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_messages/index.tsx @@ -29,7 +29,7 @@ interface UseSendMessages { } export const useSendMessages = (): UseSendMessages => { - const { assistantLangChain } = useAssistantContext(); + const { knowledgeBase } = useAssistantContext(); const [isLoading, setIsLoading] = useState(false); const sendMessages = useCallback( @@ -37,7 +37,7 @@ export const useSendMessages = (): UseSendMessages => { setIsLoading(true); try { return await fetchConnectorExecuteAction({ - assistantLangChain, + assistantLangChain: knowledgeBase.assistantLangChain, http, messages, apiConfig, @@ -46,7 +46,7 @@ export const useSendMessages = (): UseSendMessages => { setIsLoading(false); } }, - [assistantLangChain] + [knowledgeBase.assistantLangChain] ); return { isLoading, sendMessages }; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/constants.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/constants.tsx index cad3783c4669b..fbf1f68e05e0d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/constants.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/constants.tsx @@ -5,7 +5,14 @@ * 2.0. */ +import { KnowledgeBaseConfig } from '../assistant/types'; + export const DEFAULT_ASSISTANT_NAMESPACE = 'elasticAssistantDefault'; export const QUICK_PROMPT_LOCAL_STORAGE_KEY = 'quickPrompts'; export const SYSTEM_PROMPT_LOCAL_STORAGE_KEY = 'systemPrompts'; export const LAST_CONVERSATION_ID_LOCAL_STORAGE_KEY = 'lastConversationId'; +export const KNOWLEDGE_BASE_LOCAL_STORAGE_KEY = 'knowledgeBase'; + +export const DEFAULT_KNOWLEDGE_BASE_SETTINGS: KnowledgeBaseConfig = { + assistantLangChain: false, +}; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.test.tsx index 89d3e4c6e8878..7e50643ae7595 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.test.tsx @@ -28,7 +28,6 @@ const ContextWrapper: React.FC = ({ children }) => ( CodeBlockDetails[][]; baseAllow: string[]; @@ -73,6 +74,7 @@ export interface AssistantProviderProps { }) => EuiCommentProps[]; http: HttpSetup; getInitialConversations: () => Record; + modelEvaluatorEnabled?: boolean; nameSpace?: string; setConversations: React.Dispatch>>; setDefaultAllow: React.Dispatch>; @@ -87,7 +89,6 @@ export interface UseAssistantContext { augmentMessageCodeBlocks: (currentConversation: Conversation) => CodeBlockDetails[][]; allQuickPrompts: QuickPrompt[]; allSystemPrompts: Prompt[]; - assistantLangChain: boolean; baseAllow: string[]; baseAllowReplacement: string[]; docLinks: Omit; @@ -110,8 +111,10 @@ export interface UseAssistantContext { showAnonymizedValues: boolean; }) => EuiCommentProps[]; http: HttpSetup; + knowledgeBase: KnowledgeBaseConfig; localStorageLastConversationId: string | undefined; promptContexts: Record; + modelEvaluatorEnabled: boolean; nameSpace: string; registerPromptContext: RegisterPromptContext; selectedSettingsTab: SettingsTabs; @@ -120,6 +123,7 @@ export interface UseAssistantContext { setConversations: React.Dispatch>>; setDefaultAllow: React.Dispatch>; setDefaultAllowReplacement: React.Dispatch>; + setKnowledgeBase: React.Dispatch>; setLastConversationId: React.Dispatch>; setSelectedSettingsTab: React.Dispatch>; setShowAssistantOverlay: (showAssistantOverlay: ShowAssistantOverlay) => void; @@ -133,7 +137,6 @@ const AssistantContext = React.createContext(un export const AssistantProvider: React.FC = ({ actionTypeRegistry, assistantAvailability, - assistantLangChain, assistantTelemetry, augmentMessageCodeBlocks, baseAllow, @@ -149,6 +152,7 @@ export const AssistantProvider: React.FC = ({ getComments, http, getInitialConversations, + modelEvaluatorEnabled = false, nameSpace = DEFAULT_ASSISTANT_NAMESPACE, setConversations, setDefaultAllow, @@ -174,6 +178,14 @@ export const AssistantProvider: React.FC = ({ const [localStorageLastConversationId, setLocalStorageLastConversationId] = useLocalStorage(`${nameSpace}.${LAST_CONVERSATION_ID_LOCAL_STORAGE_KEY}`); + /** + * Local storage for knowledge base configuration, prefixed by assistant nameSpace + */ + const [localStorageKnowledgeBase, setLocalStorageKnowledgeBase] = useLocalStorage( + `${nameSpace}.${KNOWLEDGE_BASE_LOCAL_STORAGE_KEY}`, + DEFAULT_KNOWLEDGE_BASE_SETTINGS + ); + /** * Prompt contexts are used to provide components a way to register and make their data available to the assistant. */ @@ -254,7 +266,6 @@ export const AssistantProvider: React.FC = ({ () => ({ actionTypeRegistry, assistantAvailability, - assistantLangChain, assistantTelemetry, augmentMessageCodeBlocks, allQuickPrompts: localStorageQuickPrompts ?? [], @@ -272,6 +283,8 @@ export const AssistantProvider: React.FC = ({ docLinks, getComments, http, + knowledgeBase: localStorageKnowledgeBase ?? DEFAULT_KNOWLEDGE_BASE_SETTINGS, + modelEvaluatorEnabled, promptContexts, nameSpace, registerPromptContext, @@ -281,6 +294,7 @@ export const AssistantProvider: React.FC = ({ setConversations: onConversationsUpdated, setDefaultAllow, setDefaultAllowReplacement, + setKnowledgeBase: setLocalStorageKnowledgeBase, setSelectedSettingsTab, setShowAssistantOverlay, showAssistantOverlay, @@ -292,7 +306,6 @@ export const AssistantProvider: React.FC = ({ [ actionTypeRegistry, assistantAvailability, - assistantLangChain, assistantTelemetry, augmentMessageCodeBlocks, baseAllow, @@ -308,9 +321,11 @@ export const AssistantProvider: React.FC = ({ docLinks, getComments, http, + localStorageKnowledgeBase, localStorageLastConversationId, localStorageQuickPrompts, localStorageSystemPrompts, + modelEvaluatorEnabled, nameSpace, onConversationsUpdated, promptContexts, @@ -318,6 +333,7 @@ export const AssistantProvider: React.FC = ({ selectedSettingsTab, setDefaultAllow, setDefaultAllowReplacement, + setLocalStorageKnowledgeBase, setLocalStorageLastConversationId, setLocalStorageQuickPrompts, setLocalStorageSystemPrompts, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings/knowledge_base_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings/knowledge_base_settings.tsx new file mode 100644 index 0000000000000..2c667a5739ef6 --- /dev/null +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings/knowledge_base_settings.tsx @@ -0,0 +1,294 @@ +/* + * 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, useMemo } from 'react'; +import { + EuiFormRow, + EuiTitle, + EuiText, + EuiHorizontalRule, + EuiLoadingSpinner, + EuiSpacer, + EuiSwitchEvent, + EuiLink, + EuiBetaBadge, + EuiFlexGroup, + EuiFlexItem, + EuiHealth, + EuiButtonEmpty, + EuiSwitch, +} from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; +import * as i18n from './translations'; +import { useAssistantContext } from '../../assistant_context'; +import { useDeleteKnowledgeBase } from '../use_delete_knowledge_base/use_delete_knowledge_base'; +import { useKnowledgeBaseStatus } from '../use_knowledge_base_status/use_knowledge_base_status'; +import { useSetupKnowledgeBase } from '../use_setup_knowledge_base/use_setup_knowledge_base'; + +import type { KnowledgeBaseConfig } from '../../assistant/types'; + +const ESQL_RESOURCE = 'esql'; +const KNOWLEDGE_BASE_INDEX_PATTERN = '.kibana-elastic-ai-assistant-kb'; + +interface Props { + knowledgeBase: KnowledgeBaseConfig; + setUpdatedKnowledgeBaseSettings: React.Dispatch>; +} + +/** + * Knowledge Base Settings -- enable and disable LangChain integration, Knowledge Base, and ESQL KB Documents + */ +export const KnowledgeBaseSettings: React.FC = React.memo( + ({ knowledgeBase, setUpdatedKnowledgeBaseSettings }) => { + const { http } = useAssistantContext(); + const { + data: kbStatus, + isLoading, + isFetching, + } = useKnowledgeBaseStatus({ http, resource: ESQL_RESOURCE }); + const { mutate: setupKB, isLoading: isSettingUpKB } = useSetupKnowledgeBase({ http }); + const { mutate: deleteKB, isLoading: isDeletingUpKB } = useDeleteKnowledgeBase({ http }); + + // Resource enabled state + const isKnowledgeBaseEnabled = + (kbStatus?.index_exists && kbStatus?.pipeline_exists && kbStatus?.elser_exists) ?? false; + const isESQLEnabled = kbStatus?.esql_exists ?? false; + + // Resource availability state + const isLoadingKb = isLoading || isFetching || isSettingUpKB || isDeletingUpKB; + const isKnowledgeBaseAvailable = knowledgeBase.assistantLangChain && kbStatus?.elser_exists; + const isESQLAvailable = + knowledgeBase.assistantLangChain && isKnowledgeBaseAvailable && isKnowledgeBaseEnabled; + + // Calculated health state for EuiHealth component + const elserHealth = kbStatus?.elser_exists ? 'success' : 'subdued'; + const knowledgeBaseHealth = isKnowledgeBaseEnabled ? 'success' : 'subdued'; + const esqlHealth = isESQLEnabled ? 'success' : 'subdued'; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Main `Knowledge Base` switch, which toggles the `assistantLangChain` UI feature toggle + // setting that is saved to localstorage + const onEnableAssistantLangChainChange = useCallback( + (event: EuiSwitchEvent) => { + setUpdatedKnowledgeBaseSettings({ + ...knowledgeBase, + assistantLangChain: event.target.checked, + }); + + // If enabling and ELSER exists, try to set up automatically + if (event.target.checked && kbStatus?.elser_exists) { + setupKB(ESQL_RESOURCE); + } + }, + [kbStatus?.elser_exists, knowledgeBase, setUpdatedKnowledgeBaseSettings, setupKB] + ); + + const assistantLangChainSwitch = useMemo(() => { + return isLoadingKb ? ( + + ) : ( + + ); + }, [isLoadingKb, knowledgeBase.assistantLangChain, onEnableAssistantLangChainChange]); + + ////////////////////////////////////////////////////////////////////////////////////////// + // Knowledge Base Resource + const onEnableKB = useCallback( + (enabled: boolean) => { + if (enabled) { + setupKB(); + } else { + deleteKB(); + } + }, + [deleteKB, setupKB] + ); + + const knowledgeBaseActionButton = useMemo(() => { + return isLoadingKb || !isKnowledgeBaseAvailable ? ( + <> + ) : ( + onEnableKB(!isKnowledgeBaseEnabled)} + size="xs" + > + {isKnowledgeBaseEnabled + ? i18n.KNOWLEDGE_BASE_DELETE_BUTTON + : i18n.KNOWLEDGE_BASE_INIT_BUTTON} + + ); + }, [isKnowledgeBaseAvailable, isKnowledgeBaseEnabled, isLoadingKb, onEnableKB]); + + const knowledgeBaseDescription = useMemo(() => { + return isKnowledgeBaseEnabled ? ( + <> + {i18n.KNOWLEDGE_BASE_DESCRIPTION_INSTALLED(KNOWLEDGE_BASE_INDEX_PATTERN)}{' '} + {knowledgeBaseActionButton} + + ) : ( + <> + {i18n.KNOWLEDGE_BASE_DESCRIPTION} {knowledgeBaseActionButton} + + ); + }, [isKnowledgeBaseEnabled, knowledgeBaseActionButton]); + + ////////////////////////////////////////////////////////////////////////////////////////// + // ESQL Resource + const onEnableESQL = useCallback( + (enabled: boolean) => { + if (enabled) { + setupKB(ESQL_RESOURCE); + } else { + deleteKB(ESQL_RESOURCE); + } + }, + [deleteKB, setupKB] + ); + + const esqlActionButton = useMemo(() => { + return isLoadingKb || !isESQLAvailable ? ( + <> + ) : ( + onEnableESQL(!isESQLEnabled)} + size="xs" + > + {isESQLEnabled ? i18n.KNOWLEDGE_BASE_DELETE_BUTTON : i18n.KNOWLEDGE_BASE_INIT_BUTTON} + + ); + }, [isLoadingKb, isESQLAvailable, isESQLEnabled, onEnableESQL]); + + const esqlDescription = useMemo(() => { + return isESQLEnabled ? ( + <> + {i18n.ESQL_DESCRIPTION_INSTALLED} {esqlActionButton} + + ) : ( + <> + {i18n.ESQL_DESCRIPTION} {esqlActionButton} + + ); + }, [esqlActionButton, isESQLEnabled]); + + return ( + <> + +

+ {i18n.SETTINGS_TITLE}{' '} + +

+
+ + {i18n.SETTINGS_DESCRIPTION} + + + + {assistantLangChainSwitch} + + + + + +
+ {i18n.KNOWLEDGE_BASE_ELSER_LABEL} + + + {i18n.KNOWLEDGE_BASE_ELSER_MACHINE_LEARNING} + + ), + seeDocs: ( + + {i18n.KNOWLEDGE_BASE_ELSER_SEE_DOCS} + + ), + }} + /> + +
+
+ +
+ {i18n.KNOWLEDGE_BASE_LABEL} + + {knowledgeBaseDescription} + +
+
+ + + {i18n.ESQL_LABEL} + + {esqlDescription} + + + +
+ + ); + } +); + +KnowledgeBaseSettings.displayName = 'KnowledgeBaseSettings'; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings/translations.ts new file mode 100644 index 0000000000000..95417ddf6a889 --- /dev/null +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings/translations.ts @@ -0,0 +1,109 @@ +/* + * 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 SETTINGS_TITLE = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.settingsTitle', + { + defaultMessage: 'Knowledge Base', + } +); + +export const SETTINGS_BADGE = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.settingsBadgeTitle', + { + defaultMessage: 'Experimental', + } +); + +export const SETTINGS_DESCRIPTION = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.settingsDescription', + { + defaultMessage: + 'Powered by ELSER, the Knowledge Base enables the ability to recall documents and other relevant context within your conversation.', + } +); + +export const KNOWLEDGE_BASE_LABEL = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseLabel', + { + defaultMessage: 'Knowledge Base', + } +); + +export const KNOWLEDGE_BASE_DESCRIPTION = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseDescription', + { + defaultMessage: 'Index where Knowledge Base docs are stored', + } +); + +export const KNOWLEDGE_BASE_DESCRIPTION_INSTALLED = (kbIndexPattern: string) => + i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseInstalledDescription', + { + defaultMessage: 'Initialized to `{kbIndexPattern}`', + values: { kbIndexPattern }, + } + ); + +export const KNOWLEDGE_BASE_INIT_BUTTON = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.initializeKnowledgeBaseButton', + { + defaultMessage: 'Initialize', + } +); + +export const KNOWLEDGE_BASE_DELETE_BUTTON = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.deleteKnowledgeBaseButton', + { + defaultMessage: 'Delete', + } +); + +export const KNOWLEDGE_BASE_ELSER_LABEL = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.elserLabel', + { + defaultMessage: 'ELSER Configured', + } +); + +export const KNOWLEDGE_BASE_ELSER_MACHINE_LEARNING = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.elserMachineLearningDescription', + { + defaultMessage: 'Machine Learning', + } +); + +export const KNOWLEDGE_BASE_ELSER_SEE_DOCS = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.elserSeeDocsDescription', + { + defaultMessage: 'See docs', + } +); + +export const ESQL_LABEL = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlLabel', + { + defaultMessage: 'ES|QL Knowledge Base Documents', + } +); + +export const ESQL_DESCRIPTION = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlDescription', + { + defaultMessage: 'Knowledge Base docs for generating ES|QL queries', + } +); + +export const ESQL_DESCRIPTION_INSTALLED = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlInstalledDescription', + { + defaultMessage: 'ES|QL Knowledge Base docs loaded', + } +); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/mock/test_providers/test_providers.tsx b/x-pack/packages/kbn-elastic-assistant/impl/mock/test_providers/test_providers.tsx index 53a73ec16b661..024e39ac46c3c 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/mock/test_providers/test_providers.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/mock/test_providers/test_providers.tsx @@ -72,7 +72,6 @@ export const TestProvidersComponent: React.FC = ({ = ({ children, isILMAvailab { actionTypeRegistry={actionTypeRegistry} augmentMessageCodeBlocks={augmentMessageCodeBlocks} assistantAvailability={assistantAvailability} - // NOTE: `assistantLangChain` and `assistantModelEvaluation` experimental feature will be coupled until upcoming - // Knowledge Base UI updates, which will remove the `assistantLangChain` feature flag in favor of a UI feature toggle - assistantLangChain={isModelEvaluationEnabled} assistantTelemetry={assistantTelemetry} defaultAllow={defaultAllow} defaultAllowReplacement={defaultAllowReplacement} @@ -71,6 +68,7 @@ export const AssistantProvider: React.FC = ({ children }) => { getInitialConversations={getInitialConversation} getComments={getComments} http={http} + modelEvaluatorEnabled={isModelEvaluationEnabled} nameSpace={nameSpace} setConversations={setConversations} setDefaultAllow={setDefaultAllow} diff --git a/x-pack/plugins/security_solution/public/common/mock/mock_assistant_provider.tsx b/x-pack/plugins/security_solution/public/common/mock/mock_assistant_provider.tsx index 2954f47e0d1a8..4dc5f01b0ee7d 100644 --- a/x-pack/plugins/security_solution/public/common/mock/mock_assistant_provider.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/mock_assistant_provider.tsx @@ -34,7 +34,6 @@ export const MockAssistantProviderComponent: React.FC = ({ children }) => [])} baseAllow={[]} baseAllowReplacement={[]}