diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/index.tsx index f84c525e2d989..88c5727ee0963 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/index.tsx @@ -6,7 +6,12 @@ */ import { HttpSetup } from '@kbn/core/public'; -import { API_VERSIONS, ApiConfig, Replacements } from '@kbn/elastic-assistant-common'; +import { + API_VERSIONS, + ApiConfig, + MessageMetadata, + Replacements, +} from '@kbn/elastic-assistant-common'; import { API_ERROR } from '../translations'; import { getOptionalRequestParams } from '../helpers'; import { TraceOptions } from '../types'; @@ -34,6 +39,7 @@ export interface FetchConnectorExecuteResponse { transactionId: string; traceId: string; }; + metadata?: MessageMetadata; } export const fetchConnectorExecuteAction = async ({ @@ -110,6 +116,7 @@ export const fetchConnectorExecuteAction = async ({ transaction_id: string; trace_id: string; }; + metadata?: MessageMetadata; }>(`/internal/elastic_assistant/actions/connector/${apiConfig?.connectorId}/_execute`, { method: 'POST', body: JSON.stringify(requestBody), @@ -144,6 +151,7 @@ export const fetchConnectorExecuteAction = async ({ return { response: response.data, + metadata: response.metadata, isError: false, isStream: false, traceData, diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx index 002d70b6965ed..669c0c6f327e1 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx @@ -166,7 +166,11 @@ export const AssistantHeader: React.FC = ({ /> - + diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/translations.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/translations.ts index d38eb73b28939..38d439848ee38 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/translations.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/translations.ts @@ -84,25 +84,6 @@ export const CHAT_OPTIONS = i18n.translate( } ); -const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; - -export const ANONYMIZE_VALUES_TOOLTIP = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.anonymizeValues.tooltip', - { - values: { keyboardShortcut: isMac ? '⌥ + a' : 'Alt + a' }, - defaultMessage: - 'Toggle to reveal or hide field values in your chat stream. The data sent to the LLM is still anonymized based on settings in the Anonymization panel. Keyboard shortcut: {keyboardShortcut}', - } -); - -export const SHOW_CITATIONS_TOOLTIP = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.showCitationsLabel.tooltip', - { - values: { keyboardShortcut: isMac ? '⌥ + c' : 'Alt + c' }, - defaultMessage: 'Keyboard shortcut: {keyboardShortcut}', - } -); - export const CANCEL_BUTTON_TEXT = i18n.translate( 'xpack.elasticAssistant.assistant.resetConversationModal.cancelButtonText', { diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/utils/index.test.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/utils/index.test.ts new file mode 100644 index 0000000000000..2b4c0270c3ace --- /dev/null +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/utils/index.test.ts @@ -0,0 +1,34 @@ +/* + * 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 { alertConvo, conversationWithContentReferences } from '../../../mock/conversation'; +import { Conversation } from '../../../..'; +import { conversationContainsContentReferences, conversationContainsAnonymizedValues } from '.'; + +describe('conversation utils', () => { + it.each([ + [undefined, false], + [conversationWithContentReferences, true], + [alertConvo, false], + ])( + 'conversationContainsContentReferences', + (conversation: Conversation | undefined, expected: boolean) => { + expect(conversationContainsContentReferences(conversation)).toBe(expected); + } + ); + + it.each([ + [undefined, false], + [conversationWithContentReferences, false], + [alertConvo, true], + ])( + 'conversationContainsAnonymizedValues', + (conversation: Conversation | undefined, expected: boolean) => { + expect(conversationContainsAnonymizedValues(conversation)).toBe(expected); + } + ); +}); diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/utils/index.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/utils/index.ts new file mode 100644 index 0000000000000..ad848fff797e7 --- /dev/null +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/utils/index.ts @@ -0,0 +1,20 @@ +/* + * 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 { isEmpty } from 'lodash'; +import { Conversation } from '../../../..'; + +export const conversationContainsContentReferences = (conversation?: Conversation): boolean => { + return ( + conversation?.messages.some((message) => !isEmpty(message.metadata?.contentReferences)) ?? false + ); +}; + +/** Checks if the conversations has replacements, not if the replacements are actually used */ +export const conversationContainsAnonymizedValues = (conversation?: Conversation): boolean => { + return !isEmpty(conversation?.replacements); +}; diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/helpers.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/helpers.ts index 9265cdd9d57ec..7c4aa0482c052 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/helpers.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/helpers.ts @@ -25,6 +25,7 @@ export const getMessageFromRawResponse = ( timestamp: dateTimeString, isError, traceData: rawResponse.traceData, + metadata: rawResponse.metadata, }; } else { return { diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx index 05aadc1552732..57d39d5ba51fd 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx @@ -51,6 +51,10 @@ import { ConversationSidePanel } from './conversations/conversation_sidepanel'; import { SelectedPromptContexts } from './prompt_editor/selected_prompt_contexts'; import { AssistantHeader } from './assistant_header'; import { AnonymizedValuesAndCitationsTour } from '../tour/anonymized_values_and_citations_tour'; +import { + conversationContainsAnonymizedValues, + conversationContainsContentReferences, +} from './conversations/utils'; export const CONVERSATION_SIDE_PANEL_WIDTH = 220; @@ -226,10 +230,12 @@ const AssistantComponent: React.FC = ({ const onKeyDown = useCallback( (event: KeyboardEvent) => { if (event.altKey && event.code === 'KeyC') { + if (!conversationContainsContentReferences(currentConversation)) return; event.preventDefault(); setContentReferencesVisible(!contentReferencesVisible); } if (event.altKey && event.code === 'KeyA') { + if (!conversationContainsAnonymizedValues(currentConversation)) return; event.preventDefault(); setShowAnonymizedValues(!showAnonymizedValues); } @@ -239,6 +245,7 @@ const AssistantComponent: React.FC = ({ contentReferencesVisible, setShowAnonymizedValues, showAnonymizedValues, + currentConversation, ] ); diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/settings/settings_context_menu/settings_context_menu.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/settings/settings_context_menu/settings_context_menu.tsx index 355b65b2e4315..d273f70ce6190 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/settings/settings_context_menu/settings_context_menu.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/settings/settings_context_menu/settings_context_menu.tsx @@ -21,22 +21,40 @@ import { EuiHorizontalRule, EuiToolTip, EuiSwitchEvent, + EuiIcon, } from '@elastic/eui'; import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; +import { FormattedMessage } from '@kbn/i18n-react'; import { KnowledgeBaseTour } from '../../../tour/knowledge_base'; import { AnonymizationSettingsManagement } from '../../../data_anonymization/settings/anonymization_settings_management'; -import { useAssistantContext } from '../../../..'; +import { Conversation, useAssistantContext } from '../../../..'; import * as i18n from '../../assistant_header/translations'; import { AlertsSettingsModal } from '../alerts_settings/alerts_settings_modal'; import { KNOWLEDGE_BASE_TAB } from '../const'; import { AI_ASSISTANT_MENU } from './translations'; +import { + conversationContainsAnonymizedValues, + conversationContainsContentReferences, +} from '../../conversations/utils'; interface Params { isDisabled?: boolean; onChatCleared?: () => void; + selectedConversation?: Conversation; } +const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; + +const ConditionalWrap = ({ + condition, + wrap, + children, +}: { + condition: boolean; + wrap: (children: React.ReactElement) => React.ReactElement; + children: React.ReactElement; +}) => (condition ? wrap(children) : children); + export const SettingsContextMenu: React.FC = React.memo( ({ isDisabled = false, onChatCleared }: Params) => { const { @@ -116,6 +134,16 @@ export const SettingsContextMenu: React.FC = React.memo( [setShowAnonymizedValues] ); + const selectedConversationHasCitations = useMemo( + () => conversationContainsContentReferences(selectedConversation), + [selectedConversation] + ); + + const selectedConversationHasAnonymizedValues = useMemo( + () => conversationContainsAnonymizedValues(selectedConversation), + [selectedConversation] + ); + const items = useMemo( () => [ = React.memo(

{i18n.CHAT_OPTIONS}

- + + + ( + + } + > + {children} + + )} + > + + + + + {str}, + }} + /> + } + > + + + + +
+ + {contentReferencesEnabled && ( - + + + ( + + } + > + {children} + + )} + > + + + + + {str}, + }} + /> + } + > + + + + - - {contentReferencesEnabled && ( - - - - - )} = React.memo( knowledgeBase.latestAlerts, showDestroyModal, contentReferencesEnabled, + selectedConversationHasCitations, + selectedConversationHasAnonymizedValues, ] ); diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/index.tsx index da2bbc7469b46..83b718d999768 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/index.tsx @@ -7,12 +7,16 @@ import { EuiTourStep } from '@elastic/eui'; import React, { useCallback, useEffect, useState } from 'react'; -import { isEmpty, throttle } from 'lodash'; +import { throttle } from 'lodash'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import { Conversation } from '../../assistant_context/types'; import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '../const'; import { anonymizedValuesAndCitationsTourStep1 } from './step_config'; import { TourState } from '../knowledge_base'; +import { + conversationContainsAnonymizedValues, + conversationContainsContentReferences, +} from '../../assistant/conversations/utils'; interface Props { conversation: Conversation | undefined; @@ -49,12 +53,10 @@ export const AnonymizedValuesAndCitationsTour: React.FC = ({ conversation return; } - const containsContentReferences = conversation.messages.some( - (message) => !isEmpty(message.metadata?.contentReferences) - ); - const containsReplacements = !isEmpty(conversation.replacements); + const containsContentReferences = conversationContainsContentReferences(conversation); + const containsAnonymizedValues = conversationContainsAnonymizedValues(conversation); - if (containsContentReferences || containsReplacements) { + if (containsContentReferences || containsAnonymizedValues) { const timer = setTimeout(() => { setShowTour(true); }, 1000); diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/step_config.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/step_config.tsx index dad5326f0be2c..7ba56b8db8cdc 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/step_config.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/step_config.tsx @@ -30,18 +30,18 @@ export const anonymizedValuesAndCitationsTourStep1 = { {str}, }} /> {str}, }} />