diff --git a/x-pack/plugins/observability_solution/observability/public/application/index.tsx b/x-pack/plugins/observability_solution/observability/public/application/index.tsx index 01e96fe7415d4..79063e6927e4d 100644 --- a/x-pack/plugins/observability_solution/observability/public/application/index.tsx +++ b/x-pack/plugins/observability_solution/observability/public/application/index.tsx @@ -87,7 +87,62 @@ export const renderApp = ({ const PresentationContextProvider = plugins.presentationUtil?.ContextProvider ?? React.Fragment; const clearScreenContext = plugins.observabilityAIAssistant.service.setScreenContext({ - starterPrompts: ['Do I have any alerts?', 'How can I create a new rule?', 'What are cases?'], + starterPrompts: [ + { + title: i18n.translate( + 'xpack.observability.aiAssistant.starterPrompts.doIHaveAlerts.title', + { + defaultMessage: 'Alerting', + } + ), + prompt: i18n.translate( + 'xpack.observability.aiAssistant.starterPrompts.doIHaveAlerts.prompt', + { + defaultMessage: 'Do I have any alerts?', + } + ), + icon: 'bell', + }, + { + title: i18n.translate( + 'xpack.observability.aiAssistant.starterPrompts.howCanICreateANewRule.title', + { + defaultMessage: 'Rule creation', + } + ), + prompt: i18n.translate( + 'xpack.observability.aiAssistant.starterPrompts.howCanICreateANewRule.prompt', + { + defaultMessage: 'How can I create a new rule?', + } + ), + icon: 'createSingleMetricJob', + }, + { + title: i18n.translate('xpack.observability.aiAssistant.starterPrompts.whatAreCases.title', { + defaultMessage: 'Cases', + }), + prompt: i18n.translate( + 'xpack.observability.aiAssistant.starterPrompts.whatAreCases.prompt', + { + defaultMessage: 'What are cases?', + } + ), + icon: 'casesApp', + }, + { + title: i18n.translate('xpack.observability.aiAssistant.starterPrompts.whatAreSlos.title', { + defaultMessage: 'SLOs', + }), + prompt: i18n.translate( + 'xpack.observability.aiAssistant.starterPrompts.whatAreSlos.prompt', + { + defaultMessage: 'What are SLOs?', + } + ), + icon: 'bullseye', + }, + ], }); ReactDOM.render( diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts index ad3765d63ceb0..26c10409be9e9 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { IconType } from '@elastic/eui'; import type { ObservabilityAIAssistantChatService } from '../public'; import type { CompatibleJSONSchema, FunctionResponse } from './functions/types'; @@ -104,6 +105,12 @@ export interface ScreenContextActionDefinition { respond: ScreenContextActionRespondFunction; } +export interface StarterPrompt { + title: string; + prompt: string; + icon: IconType; +} + export interface ObservabilityAIAssistantScreenContext { screenDescription?: string; data?: Array<{ @@ -112,5 +119,5 @@ export interface ObservabilityAIAssistantScreenContext { value: any; }>; actions?: ScreenContextActionDefinition[]; - starterPrompts?: string[]; + starterPrompts?: StarterPrompt[]; } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/plugin.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant/public/plugin.tsx index c003856b03699..cf28829494136 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/plugin.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/plugin.tsx @@ -10,6 +10,7 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import type { Logger } from '@kbn/logging'; import { withSuspense } from '@kbn/shared-ux-utility'; import React, { type ComponentType, lazy, type Ref } from 'react'; +import { i18n } from '@kbn/i18n'; import { registerTelemetryEventTypes } from './analytics'; import { ObservabilityAIAssistantChatServiceContext } from './context/observability_ai_assistant_chat_service_context'; import { ObservabilityAIAssistantMultipaneFlyoutContext } from './context/observability_ai_assistant_multipane_flyout_context'; @@ -41,6 +42,7 @@ export class ObservabilityAIAssistantPlugin { logger: Logger; service?: ObservabilityAIAssistantService; + clearScreenContext?: () => void; constructor(context: PluginInitializerContext) { this.logger = context.logger.get(); @@ -64,6 +66,66 @@ export class ObservabilityAIAssistantPlugin enabled: coreStart.application.capabilities.observabilityAIAssistant.show === true, })); + this.clearScreenContext = service.setScreenContext({ + starterPrompts: [ + { + title: i18n.translate( + 'xpack.observabilityAiAssistant.starterPrompts.doIHaveAlerts.title', + { defaultMessage: 'Alerts' } + ), + prompt: i18n.translate( + 'xpack.observabilityAiAssistant.starterPrompts.doIHaveAlerts.prompt', + { + defaultMessage: 'Do I have any alerts?', + } + ), + icon: 'bell', + }, + { + title: i18n.translate( + 'xpack.observabilityAiAssistant.starterPrompts.howCanICreateANewRule.title', + { + defaultMessage: 'Rule creation', + } + ), + prompt: i18n.translate( + 'xpack.observabilityAiAssistant.starterPrompts.howCanICreateANewRule.prompt', + { + defaultMessage: 'How can I create a new rule?', + } + ), + icon: 'createSingleMetricJob', + }, + { + title: i18n.translate( + 'xpack.observabilityAiAssistant.starterPrompts.whatAreCases.title', + { + defaultMessage: 'Cases', + } + ), + prompt: i18n.translate( + 'xpack.observabilityAiAssistant.starterPrompts.whatAreCases.prompt', + { + defaultMessage: 'What are cases?', + } + ), + icon: 'casesApp', + }, + { + title: i18n.translate('xpack.observabilityAiAssistant.starterPrompts.whatAreSlos.title', { + defaultMessage: 'SLOs', + }), + prompt: i18n.translate( + 'xpack.observabilityAiAssistant.starterPrompts.whatAreSlos.prompt', + { + defaultMessage: 'What are SLOs?', + } + ), + icon: 'bullseye', + }, + ], + }); + const withProviders =

( Component: ComponentType

, services: Omit & { @@ -111,4 +173,8 @@ export class ObservabilityAIAssistantPlugin createScreenContextAction, }; } + + stop() { + this.clearScreenContext?.(); + } } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_body.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_body.tsx index a1d104b658a86..bcc0a6fde8671 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_body.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_body.tsx @@ -37,13 +37,11 @@ import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base'; import { useLicense } from '../../hooks/use_license'; import { useObservabilityAIAssistantChatService } from '../../hooks/use_observability_ai_assistant_chat_service'; import { ASSISTANT_SETUP_TITLE, EMPTY_CONVERSATION_TITLE, UPGRADE_LICENSE_TITLE } from '../../i18n'; -import { nonNullable } from '../../utils/non_nullable'; import { PromptEditor } from '../prompt_editor/prompt_editor'; import { FlyoutPositionMode } from './chat_flyout'; import { ChatHeader } from './chat_header'; import { ChatTimeline } from './chat_timeline'; import { IncorrectLicensePanel } from './incorrect_license_panel'; -import { StarterPrompts } from './starter_prompts'; import { WelcomeMessage } from './welcome_message'; const fullHeightClassName = css` @@ -356,54 +354,30 @@ export function ChatBody({ } /> ) : ( - <> - { - setStickToBottom(true); - const indexOf = messages.indexOf(editedMessage); - next(messages.slice(0, indexOf).concat(newMessage)); - }} - onFeedback={handleFeedback} - onRegenerate={(message) => { - next(reverseToLastUserMessage(messages, message)); - }} - onSendTelemetry={(eventWithPayload) => - chatService.sendAnalyticsEvent(eventWithPayload) - } - onStopGenerating={() => { - stop(); - }} - onActionClick={handleActionClick} - /> - - {!isLoading ? ( - <> - message.message.role === MessageRole.User) - .map((message) => message.message.content) - .filter(nonNullable)} - onSelectPrompt={(message) => - next( - messages.concat([ - { - '@timestamp': new Date().toISOString(), - message: { content: message, role: MessageRole.User }, - }, - ]) - ) - } - /> - - - ) : null} - + { + setStickToBottom(true); + const indexOf = messages.indexOf(editedMessage); + next(messages.slice(0, indexOf).concat(newMessage)); + }} + onFeedback={handleFeedback} + onRegenerate={(message) => { + next(reverseToLastUserMessage(messages, message)); + }} + onSendTelemetry={(eventWithPayload) => + chatService.sendAnalyticsEvent(eventWithPayload) + } + onStopGenerating={() => { + stop(); + }} + onActionClick={handleActionClick} + /> )} diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/starter_prompts.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/starter_prompts.tsx index 7185a515539ac..0367a3a9e0b18 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/starter_prompts.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/starter_prompts.tsx @@ -5,7 +5,15 @@ * 2.0. */ import React, { useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPanel, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; import { css } from '@emotion/css'; import { useObservabilityAIAssistantAppService } from '../../hooks/use_observability_ai_assistant_app_service'; import { nonNullable } from '../../utils/non_nullable'; @@ -15,28 +23,26 @@ const starterPromptClassName = css` min-width: calc(50% - 8px); `; -export function StarterPrompts({ - userPrompts, - onSelectPrompt, -}: { - userPrompts: string[]; - onSelectPrompt: (prompt: string) => void; -}) { +const starterPromptInnerClassName = css` + text-align: center !important; +`; + +export function StarterPrompts({ onSelectPrompt }: { onSelectPrompt: (prompt: string) => void }) { const service = useObservabilityAIAssistantAppService(); + const contexts = service.getScreenContexts(); + const starterPrompts = useMemo( () => [ ...new Set( - service - .getScreenContexts() + contexts .reverse() .flatMap((context) => context.starterPrompts) .filter(nonNullable) - .filter((prompt) => !userPrompts.includes(prompt)) .slice(0, 4) ), ], - [service, userPrompts] + [contexts] ); const handleSelectPrompt = (prompt: string) => { @@ -45,10 +51,22 @@ export function StarterPrompts({ return ( - {starterPrompts.map((prompt) => ( + {starterPrompts.map(({ prompt, title, icon }) => ( - handleSelectPrompt(prompt)}> - {prompt} + handleSelectPrompt(prompt)} + className={starterPromptInnerClassName} + > + + + + +

{title}

+ + {prompt} ))} diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/welcome_message.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/welcome_message.tsx index f8f3cfadd5e7e..18f4c5598c6fd 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/welcome_message.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/welcome_message.tsx @@ -92,7 +92,7 @@ export function WelcomeMessage({ - + diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/plugin.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/plugin.tsx index bfc1f288ffcbb..dae0d36b23240 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/plugin.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/plugin.tsx @@ -75,6 +75,69 @@ export class ObservabilityAIAssistantAppPlugin >, ]); + const clearScreenContext = this.appService?.setScreenContext({ + starterPrompts: [ + { + title: i18n.translate( + 'xpack.observabilityAiAssistant.app.starterPrompts.doIHaveAlerts.title', + { defaultMessage: 'Alerts' } + ), + prompt: i18n.translate( + 'xpack.observabilityAiAssistant.app.starterPrompts.doIHaveAlerts.prompt', + { + defaultMessage: 'Do I have any alerts?', + } + ), + icon: 'bell', + }, + { + title: i18n.translate( + 'xpack.observabilityAiAssistant.app.starterPrompts.howCanICreateANewRule.title', + { + defaultMessage: 'Rule creation', + } + ), + prompt: i18n.translate( + 'xpack.observabilityAiAssistant.app.starterPrompts.howCanICreateANewRule.prompt', + { + defaultMessage: 'How can I create a new rule?', + } + ), + icon: 'createSingleMetricJob', + }, + { + title: i18n.translate( + 'xpack.observabilityAiAssistant.app.starterPrompts.whatAreCases.title', + { + defaultMessage: 'Cases', + } + ), + prompt: i18n.translate( + 'xpack.observabilityAiAssistant.app.starterPrompts.whatAreCases.prompt', + { + defaultMessage: 'What are cases?', + } + ), + icon: 'casesApp', + }, + { + title: i18n.translate( + 'xpack.observabilityAiAssistant.app.starterPrompts.whatAreSlos.title', + { + defaultMessage: 'SLOs', + } + ), + prompt: i18n.translate( + 'xpack.observabilityAiAssistant.app.starterPrompts.whatAreSlos.prompt', + { + defaultMessage: 'What are SLOs?', + } + ), + icon: 'bullseye', + }, + ], + }); + ReactDOM.render( { + clearScreenContext?.(); ReactDOM.unmountComponentAtNode(appMountParameters.element); }; }, diff --git a/x-pack/plugins/observability_solution/slo/public/application.tsx b/x-pack/plugins/observability_solution/slo/public/application.tsx index 2e2d01f83409d..2fd45e754b310 100644 --- a/x-pack/plugins/observability_solution/slo/public/application.tsx +++ b/x-pack/plugins/observability_solution/slo/public/application.tsx @@ -78,6 +78,13 @@ export const renderApp = ({ const PresentationContextProvider = plugins.presentationUtil?.ContextProvider ?? React.Fragment; + const unregisterPrompts = plugins.observabilityAIAssistant.service.setScreenContext({ + starterPrompts: [ + { title: 'Getting started', icon: 'bullseye', prompt: 'What is an SLO?' }, + { title: 'Getting started', icon: 'questionInCircle', prompt: 'Can you create an SLO?' }, + ], + }); + ReactDOM.render( @@ -132,6 +139,7 @@ export const renderApp = ({ // via the ExploratoryView app, which uses search sessions. Therefore on unmounting we need to clear // these sessions. plugins.data.search.session.clear(); + unregisterPrompts(); ReactDOM.unmountComponentAtNode(element); }; }; diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slos/components/slo_list.tsx index d24324f5d6e49..c0a3d573577b2 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slos/components/slo_list.tsx @@ -80,7 +80,6 @@ export function SloList() { : '' } `), - starterPrompts: ['What is an SLO?', 'How do I create an SLO?'], }); }, [sloList, setScreenContext]);