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

[Observability AI Assistant] Create first Starter prompts #178907

Merged
merged 18 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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 @@ -86,6 +86,65 @@ export const renderApp = ({
const CloudProvider = plugins.cloud?.CloudContextProvider ?? React.Fragment;
const PresentationContextProvider = plugins.presentationUtil?.ContextProvider ?? React.Fragment;

const clearScreenContext = plugins.observabilityAIAssistant.service.setScreenContext({
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',
CoenWarmer marked this conversation as resolved.
Show resolved Hide resolved
},
{
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(
<PresentationContextProvider>
<EuiErrorBoundary>
Expand Down Expand Up @@ -141,6 +200,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();
clearScreenContext();
ReactDOM.unmountComponentAtNode(element);
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -104,6 +105,12 @@ export interface ScreenContextActionDefinition<TArguments = undefined> {
respond: ScreenContextActionRespondFunction<TArguments>;
}

export interface StarterPrompt {
title: string;
prompt: string;
icon: IconType;
}

export interface ObservabilityAIAssistantScreenContext {
screenDescription?: string;
data?: Array<{
Expand All @@ -112,4 +119,5 @@ export interface ObservabilityAIAssistantScreenContext {
value: any;
}>;
actions?: ScreenContextActionDefinition[];
starterPrompts?: StarterPrompt[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -41,6 +42,7 @@ export class ObservabilityAIAssistantPlugin
{
logger: Logger;
service?: ObservabilityAIAssistantService;
clearScreenContext?: () => void;

constructor(context: PluginInitializerContext<ConfigSchema>) {
this.logger = context.logger.get();
Expand All @@ -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 = <P extends {}, R = {}>(
Component: ComponentType<P>,
services: Omit<CoreStart, 'plugins'> & {
Expand Down Expand Up @@ -111,4 +173,8 @@ export class ObservabilityAIAssistantPlugin
createScreenContextAction,
};
}

stop() {
this.clearScreenContext?.();
CoenWarmer marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,20 @@ export function ChatBody({
className={animClassName}
>
{connectors.connectors?.length === 0 || messages.length === 1 ? (
<WelcomeMessage connectors={connectors} knowledgeBase={knowledgeBase} />
<WelcomeMessage
connectors={connectors}
knowledgeBase={knowledgeBase}
onSelectPrompt={(message) =>
next(
messages.concat([
{
'@timestamp': new Date().toISOString(),
message: { content: message, role: MessageRole.User },
},
])
)
}
/>
) : (
<ChatTimeline
messages={messages}
Expand All @@ -360,7 +373,9 @@ export function ChatBody({
onSendTelemetry={(eventWithPayload) =>
chatService.sendAnalyticsEvent(eventWithPayload)
}
onStopGenerating={stop}
onStopGenerating={() => {
stop();
}}
CoenWarmer marked this conversation as resolved.
Show resolved Hide resolved
onActionClick={handleActionClick}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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, { useMemo } from 'react';
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';

const starterPromptClassName = css`
max-width: 50%;
min-width: calc(50% - 8px);
`;

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(
CoenWarmer marked this conversation as resolved.
Show resolved Hide resolved
() => [
...new Set(
contexts
.reverse()
.flatMap((context) => context.starterPrompts)
.filter(nonNullable)
.slice(0, 4)
),
],
[contexts]
);

const handleSelectPrompt = (prompt: string) => {
onSelectPrompt(prompt);
};

return (
<EuiFlexGroup direction="row" gutterSize="m" wrap>
{starterPrompts.map(({ prompt, title, icon }) => (
<EuiFlexItem key={prompt} className={starterPromptClassName}>
<EuiPanel
paddingSize="m"
hasShadow={false}
hasBorder
onClick={() => handleSelectPrompt(prompt)}
CoenWarmer marked this conversation as resolved.
Show resolved Hide resolved
className={starterPromptInnerClassName}
>
<EuiSpacer size="s" />
<EuiIcon type={icon} size="xl" />
<EuiSpacer size="s" />
<EuiTitle size="xs">
<h2>{title}</h2>
</EuiTitle>
<EuiText size="s">{prompt}</EuiText>
</EuiPanel>
</EuiFlexItem>
))}
</EuiFlexGroup>
);
}
Loading