mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
# Backport This will backport the following commits from `main` to `8.12`: - [Onboarding Polish (#172974)](https://github.com/elastic/kibana/pull/172974) <!--- Backport version: 8.9.7 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Coen Warmer","email":"coen.warmer@gmail.com"},"sourceCommit":{"committedDate":"2023-12-13T13:00:40Z","message":"Onboarding Polish (#172974)","sha":"d28da807121b43f4c4b8a5a61b170e8f740f076b","branchLabelMapping":{"^v8.13.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport:prev-minor","v8.12.0","v8.13.0"],"number":172974,"url":"https://github.com/elastic/kibana/pull/172974","mergeCommit":{"message":"Onboarding Polish (#172974)","sha":"d28da807121b43f4c4b8a5a61b170e8f740f076b"}},"sourceBranch":"main","suggestedTargetBranches":["8.12"],"targetPullRequestStates":[{"branch":"8.12","label":"v8.12.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.13.0","labelRegex":"^v8.13.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/172974","number":172974,"mergeCommit":{"message":"Onboarding Polish (#172974)","sha":"d28da807121b43f4c4b8a5a61b170e8f740f076b"}}]}] BACKPORT--> Co-authored-by: Coen Warmer <coen.warmer@gmail.com>
This commit is contained in:
parent
5889e7dd07
commit
08a925ad85
22 changed files with 1115 additions and 419 deletions
Binary file not shown.
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 93 KiB |
|
@ -48,7 +48,7 @@ export function ObservabilityAIAssistantActionMenuItem() {
|
|||
{!isOpen || chatService.value ? (
|
||||
<AssistantAvatar size="xs" />
|
||||
) : (
|
||||
<EuiLoadingSpinner size="s" />
|
||||
<EuiLoadingSpinner size="m" />
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
|
|
@ -85,7 +85,7 @@ export function AskAssistantButton({
|
|||
<EuiToolTip
|
||||
position="top"
|
||||
title={i18n.translate('xpack.observabilityAiAssistant.askAssistantButton.popoverTitle', {
|
||||
defaultMessage: 'Elastic Assistant',
|
||||
defaultMessage: 'AI Assistant for Observability',
|
||||
})}
|
||||
content={i18n.translate(
|
||||
'xpack.observabilityAiAssistant.askAssistantButton.popoverContent',
|
||||
|
@ -98,7 +98,7 @@ export function AskAssistantButton({
|
|||
aria-label={i18n.translate(
|
||||
'xpack.observabilityAiAssistant.askAssistantButton.popoverTitle',
|
||||
{
|
||||
defaultMessage: 'Elastic Assistant',
|
||||
defaultMessage: 'AI Assistant for Observability',
|
||||
}
|
||||
)}
|
||||
data-test-subj="observabilityAiAssistantAskAssistantButtonButtonIcon"
|
||||
|
|
|
@ -70,6 +70,15 @@ export function ChatActionsMenu({
|
|||
defaultMessage: 'Actions',
|
||||
}),
|
||||
items: [
|
||||
{
|
||||
name: i18n.translate('xpack.observabilityAiAssistant.chatHeader.actions.settings', {
|
||||
defaultMessage: 'AI Assistant Settings',
|
||||
}),
|
||||
onClick: () => {
|
||||
toggleActionsMenu();
|
||||
handleNavigateToSettings();
|
||||
},
|
||||
},
|
||||
{
|
||||
name: (
|
||||
<div className="eui-textTruncate">
|
||||
|
@ -86,15 +95,6 @@ export function ChatActionsMenu({
|
|||
),
|
||||
panel: 1,
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.observabilityAiAssistant.chatHeader.actions.settings', {
|
||||
defaultMessage: 'AI Assistant Settings',
|
||||
}),
|
||||
onClick: () => {
|
||||
toggleActionsMenu();
|
||||
handleNavigateToSettings();
|
||||
},
|
||||
},
|
||||
{
|
||||
name: i18n.translate(
|
||||
'xpack.observabilityAiAssistant.chatHeader.actions.knowledgeBase',
|
||||
|
|
|
@ -5,44 +5,48 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { css, keyframes } from '@emotion/css';
|
||||
import {
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiHorizontalRule,
|
||||
EuiLoadingSpinner,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { css } from '@emotion/css';
|
||||
import type { AuthenticatedUser } from '@kbn/security-plugin/common';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Conversation, Message, MessageRole } from '../../../common/types';
|
||||
import { ChatState } from '../../hooks/use_chat';
|
||||
import { useConversation } from '../../hooks/use_conversation';
|
||||
import type { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors';
|
||||
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 { StartedFrom } from '../../utils/get_timeline_items_from_conversation';
|
||||
import type { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors';
|
||||
import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base';
|
||||
import { type Conversation, type Message, MessageRole } from '../../../common/types';
|
||||
import { ChatHeader } from './chat_header';
|
||||
import { ChatPromptEditor } from './chat_prompt_editor';
|
||||
import { ChatTimeline } from './chat_timeline';
|
||||
import { IncorrectLicensePanel } from './incorrect_license_panel';
|
||||
import { InitialSetupPanel } from './initial_setup_panel';
|
||||
import { ChatActionClickType } from './types';
|
||||
import { EMPTY_CONVERSATION_TITLE } from '../../i18n';
|
||||
import { Feedback } from '../feedback_buttons';
|
||||
import { IncorrectLicensePanel } from './incorrect_license_panel';
|
||||
import { WelcomeMessage } from './welcome_message';
|
||||
import { EMPTY_CONVERSATION_TITLE } from '../../i18n';
|
||||
import { MESSAGE_FEEDBACK } from '../../analytics/schema';
|
||||
import { ChatActionClickType } from './types';
|
||||
import type { StartedFrom } from '../../utils/get_timeline_items_from_conversation';
|
||||
|
||||
const fullHeightClassName = css`
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const timelineClassName = css`
|
||||
overflow-y: auto;
|
||||
`;
|
||||
|
||||
const loadingSpinnerContainerClassName = css`
|
||||
align-self: center;
|
||||
const promptEditorClassname = css`
|
||||
overflow: hidden;
|
||||
transition: height ${euiThemeVars.euiAnimSpeedFast} ${euiThemeVars.euiAnimSlightResistance};
|
||||
`;
|
||||
|
||||
const incorrectLicenseContainer = css`
|
||||
|
@ -54,6 +58,29 @@ const chatBodyContainerClassNameWithError = css`
|
|||
align-self: center;
|
||||
`;
|
||||
|
||||
const promptEditorContainerClassName = css`
|
||||
padding-top: 12px;
|
||||
padding-bottom: 8px;
|
||||
`;
|
||||
|
||||
const fadeInAnimation = keyframes`
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
`;
|
||||
|
||||
const animClassName = css`
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
animation: ${fadeInAnimation} ${euiThemeVars.euiAnimSpeedNormal}
|
||||
${euiThemeVars.euiAnimSlightBounce} ${euiThemeVars.euiAnimSpeedNormal} forwards;
|
||||
`;
|
||||
|
||||
export function ChatBody({
|
||||
initialTitle,
|
||||
initialMessages,
|
||||
|
@ -175,83 +202,86 @@ export function ChatBody({
|
|||
</EuiFlexItem>
|
||||
</>
|
||||
);
|
||||
} else if (
|
||||
connectors.loading ||
|
||||
knowledgeBase.status.loading ||
|
||||
(!conversation.value && conversation.loading)
|
||||
) {
|
||||
footer = (
|
||||
<EuiFlexItem className={loadingSpinnerContainerClassName}>
|
||||
<EuiLoadingSpinner />
|
||||
</EuiFlexItem>
|
||||
);
|
||||
} else if (connectors.connectors?.length === 0 && !initialConversationId) {
|
||||
footer = (
|
||||
<InitialSetupPanel
|
||||
connectors={connectors}
|
||||
connectorsManagementHref={connectorsManagementHref}
|
||||
knowledgeBase={knowledgeBase}
|
||||
startedFrom={startedFrom}
|
||||
/>
|
||||
);
|
||||
} else if (!conversation.value && conversation.loading) {
|
||||
footer = null;
|
||||
} else {
|
||||
footer = (
|
||||
<>
|
||||
<EuiFlexItem grow className={timelineClassName}>
|
||||
<div ref={timelineContainerRef}>
|
||||
<EuiPanel hasBorder={false} hasShadow={false} paddingSize="m">
|
||||
<ChatTimeline
|
||||
startedFrom={startedFrom}
|
||||
messages={messages}
|
||||
knowledgeBase={knowledgeBase}
|
||||
chatService={chatService}
|
||||
currentUser={currentUser}
|
||||
chatState={state}
|
||||
hasConnector={!!connectors.connectors?.length}
|
||||
onEdit={(editedMessage, newMessage) => {
|
||||
const indexOf = messages.indexOf(editedMessage);
|
||||
next(messages.slice(0, indexOf).concat(newMessage));
|
||||
}}
|
||||
onFeedback={handleFeedback}
|
||||
onRegenerate={(message) => {
|
||||
const indexOf = messages.indexOf(message);
|
||||
next(messages.slice(0, indexOf));
|
||||
}}
|
||||
onStopGenerating={() => {
|
||||
stop();
|
||||
}}
|
||||
onActionClick={(payload) => {
|
||||
setStickToBottom(true);
|
||||
switch (payload.type) {
|
||||
case ChatActionClickType.executeEsqlQuery:
|
||||
next(
|
||||
messages.concat({
|
||||
'@timestamp': new Date().toISOString(),
|
||||
message: {
|
||||
role: MessageRole.Assistant,
|
||||
content: '',
|
||||
function_call: {
|
||||
name: 'execute_query',
|
||||
arguments: JSON.stringify({
|
||||
query: payload.query,
|
||||
}),
|
||||
trigger: MessageRole.User,
|
||||
<div ref={timelineContainerRef} className={fullHeightClassName}>
|
||||
<EuiPanel
|
||||
grow
|
||||
hasBorder={false}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
className={animClassName}
|
||||
>
|
||||
{connectors.connectors?.length === 0 || messages.length === 1 ? (
|
||||
<WelcomeMessage connectors={connectors} knowledgeBase={knowledgeBase} />
|
||||
) : (
|
||||
<ChatTimeline
|
||||
startedFrom={startedFrom}
|
||||
messages={messages}
|
||||
knowledgeBase={knowledgeBase}
|
||||
chatService={chatService}
|
||||
currentUser={currentUser}
|
||||
chatState={state}
|
||||
hasConnector={!!connectors.connectors?.length}
|
||||
onEdit={(editedMessage, newMessage) => {
|
||||
const indexOf = messages.indexOf(editedMessage);
|
||||
next(messages.slice(0, indexOf).concat(newMessage));
|
||||
}}
|
||||
onFeedback={handleFeedback}
|
||||
onRegenerate={(message) => {
|
||||
const indexOf = messages.indexOf(message);
|
||||
next(messages.slice(0, indexOf));
|
||||
}}
|
||||
onStopGenerating={() => {
|
||||
stop();
|
||||
}}
|
||||
onActionClick={(payload) => {
|
||||
setStickToBottom(true);
|
||||
switch (payload.type) {
|
||||
case ChatActionClickType.executeEsqlQuery:
|
||||
next(
|
||||
messages.concat({
|
||||
'@timestamp': new Date().toISOString(),
|
||||
message: {
|
||||
role: MessageRole.Assistant,
|
||||
content: '',
|
||||
function_call: {
|
||||
name: 'execute_query',
|
||||
arguments: JSON.stringify({
|
||||
query: payload.query,
|
||||
}),
|
||||
trigger: MessageRole.User,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</EuiPanel>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
className={promptEditorClassname}
|
||||
style={{
|
||||
height: !connectors.loading && connectors.connectors?.length !== 0 ? 110 : 0,
|
||||
}}
|
||||
>
|
||||
<EuiHorizontalRule margin="none" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiPanel hasBorder={false} hasShadow={false} paddingSize="m">
|
||||
<EuiPanel
|
||||
hasBorder={false}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
className={promptEditorContainerClassName}
|
||||
>
|
||||
<ChatPromptEditor
|
||||
loading={isLoading}
|
||||
disabled={!connectors.selectedConnector || !hasCorrectLicense}
|
||||
|
|
|
@ -39,11 +39,12 @@ export function ChatFlyout({
|
|||
startedFrom: StartedFrom;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const {
|
||||
services: { http },
|
||||
} = useKibana();
|
||||
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
const currentUser = useCurrentUser();
|
||||
|
||||
const connectors = useGenAIConnectors();
|
||||
|
@ -54,6 +55,12 @@ export function ChatFlyout({
|
|||
|
||||
const [conversationId, setConversationId] = useState<string | undefined>(undefined);
|
||||
|
||||
const conversationsHeaderClassName = css`
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: solid 1px ${euiTheme.border.color};
|
||||
`;
|
||||
|
||||
return isOpen ? (
|
||||
<EuiFlyout onClose={onClose}>
|
||||
<EuiFlexGroup
|
||||
|
@ -67,7 +74,7 @@ export function ChatFlyout({
|
|||
hasShadow={false}
|
||||
hasBorder={false}
|
||||
borderRadius="none"
|
||||
css={{ borderBottom: `solid 1px ${euiTheme.border.color}` }}
|
||||
className={conversationsHeaderClassName}
|
||||
>
|
||||
{conversationId ? (
|
||||
<EuiLink
|
||||
|
|
|
@ -28,6 +28,11 @@ const minWidthClassName = css`
|
|||
min-width: 0;
|
||||
`;
|
||||
|
||||
const chatHeaderClassName = css`
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
`;
|
||||
|
||||
export function ChatHeader({
|
||||
title,
|
||||
loading,
|
||||
|
@ -68,7 +73,13 @@ export function ChatHeader({
|
|||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
return (
|
||||
<EuiPanel paddingSize="m" hasBorder={false} hasShadow={false} borderRadius="none">
|
||||
<EuiPanel
|
||||
borderRadius="none"
|
||||
hasBorder={false}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
className={chatHeaderClassName}
|
||||
>
|
||||
<EuiFlexGroup gutterSize="m" responsive={false} alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
{loading ? <EuiLoadingSpinner size="l" /> : <AssistantAvatar size="s" />}
|
||||
|
|
|
@ -9,19 +9,18 @@ import React, { ReactNode, useMemo } from 'react';
|
|||
import { css } from '@emotion/css';
|
||||
import { EuiCommentList } from '@elastic/eui';
|
||||
import type { AuthenticatedUser } from '@kbn/security-plugin/common';
|
||||
import { ChatItem } from './chat_item';
|
||||
import { ChatWelcomePanel } from './chat_welcome_panel';
|
||||
import { ChatConsolidatedItems } from './chat_consolidated_items';
|
||||
import type { Feedback } from '../feedback_buttons';
|
||||
import { type Message } from '../../../common';
|
||||
import type { Message } from '../../../common';
|
||||
import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base';
|
||||
import type { ChatActionClickHandler } from './types';
|
||||
import type { ObservabilityAIAssistantChatService } from '../../types';
|
||||
import { ChatItem } from './chat_item';
|
||||
import { ChatConsolidatedItems } from './chat_consolidated_items';
|
||||
import { ChatState } from '../../hooks/use_chat';
|
||||
import {
|
||||
getTimelineItemsfromConversation,
|
||||
StartedFrom,
|
||||
} from '../../utils/get_timeline_items_from_conversation';
|
||||
import { ObservabilityAIAssistantChatService } from '../../types';
|
||||
import { ChatState } from '../../hooks/use_chat';
|
||||
|
||||
export interface ChatTimelineItem
|
||||
extends Pick<Message['message'], 'role' | 'content' | 'function_call'> {
|
||||
|
@ -107,44 +106,40 @@ export function ChatTimeline({
|
|||
|
||||
return (
|
||||
<EuiCommentList
|
||||
css={css`
|
||||
className={css`
|
||||
padding-bottom: 32px;
|
||||
`}
|
||||
>
|
||||
{items.length <= 1 ? (
|
||||
<ChatWelcomePanel knowledgeBase={knowledgeBase} />
|
||||
) : (
|
||||
items.map((item, index) => {
|
||||
return Array.isArray(item) ? (
|
||||
<ChatConsolidatedItems
|
||||
key={index}
|
||||
consolidatedItem={item}
|
||||
onFeedback={onFeedback}
|
||||
onRegenerate={onRegenerate}
|
||||
onEditSubmit={onEdit}
|
||||
onStopGenerating={onStopGenerating}
|
||||
onActionClick={onActionClick}
|
||||
/>
|
||||
) : (
|
||||
<ChatItem
|
||||
// use index, not id to prevent unmounting of component when message is persisted
|
||||
key={index}
|
||||
{...item}
|
||||
onFeedbackClick={(feedback) => {
|
||||
onFeedback(item.message, feedback);
|
||||
}}
|
||||
onRegenerateClick={() => {
|
||||
onRegenerate(item.message);
|
||||
}}
|
||||
onEditSubmit={(message) => {
|
||||
onEdit(item.message, message);
|
||||
}}
|
||||
onStopGeneratingClick={onStopGenerating}
|
||||
onActionClick={onActionClick}
|
||||
/>
|
||||
);
|
||||
})
|
||||
)}
|
||||
{items.map((item, index) => {
|
||||
return Array.isArray(item) ? (
|
||||
<ChatConsolidatedItems
|
||||
key={index}
|
||||
consolidatedItem={item}
|
||||
onFeedback={onFeedback}
|
||||
onRegenerate={onRegenerate}
|
||||
onEditSubmit={onEdit}
|
||||
onStopGenerating={onStopGenerating}
|
||||
onActionClick={onActionClick}
|
||||
/>
|
||||
) : (
|
||||
<ChatItem
|
||||
// use index, not id to prevent unmounting of component when message is persisted
|
||||
key={index}
|
||||
{...item}
|
||||
onFeedbackClick={(feedback) => {
|
||||
onFeedback(item.message, feedback);
|
||||
}}
|
||||
onRegenerateClick={() => {
|
||||
onRegenerate(item.message);
|
||||
}}
|
||||
onEditSubmit={(message) => {
|
||||
onEdit(item.message, message);
|
||||
}}
|
||||
onStopGeneratingClick={onStopGenerating}
|
||||
onActionClick={onActionClick}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</EuiCommentList>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,52 +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 from 'react';
|
||||
import { EuiButton, EuiFlexGroup, EuiPanel } from '@elastic/eui';
|
||||
import { css } from '@emotion/css';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import { Disclaimer } from './disclaimer';
|
||||
import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base';
|
||||
|
||||
const incorrectLicenseContainer = css`
|
||||
height: 100%;
|
||||
padding: ${euiThemeVars.euiPanelPaddingModifiers.paddingMedium};
|
||||
`;
|
||||
|
||||
export function ChatWelcomePanel({ knowledgeBase }: { knowledgeBase: UseKnowledgeBaseResult }) {
|
||||
return (
|
||||
<EuiPanel hasBorder={false} hasShadow={false}>
|
||||
<EuiFlexGroup
|
||||
direction="column"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
className={incorrectLicenseContainer}
|
||||
>
|
||||
<Disclaimer />
|
||||
|
||||
{!knowledgeBase.status.value?.ready ? (
|
||||
<EuiButton
|
||||
data-test-subj="observabilityAiAssistantChatWelcomePanelSetUpKnowledgeBaseButton"
|
||||
color="primary"
|
||||
fill
|
||||
iconType={knowledgeBase.status.value?.ready ? 'checkInCircleFilled' : 'dotInCircle'}
|
||||
isLoading={knowledgeBase.isInstalling || knowledgeBase.status.loading}
|
||||
onClick={knowledgeBase.install}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.chatWelcomePanel.knowledgeBase.buttonLabel.notInstalledYet',
|
||||
{
|
||||
defaultMessage: 'Set up knowledge base',
|
||||
}
|
||||
)}
|
||||
</EuiButton>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
|
@ -6,27 +6,21 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiImage, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import { EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import ctaImage from '../../assets/elastic_ai_assistant.png';
|
||||
|
||||
export function Disclaimer() {
|
||||
return (
|
||||
<>
|
||||
<EuiImage src={ctaImage} alt="Elastic AI Assistant" size="m" />
|
||||
<EuiTitle>
|
||||
<h2>
|
||||
{i18n.translate('xpack.observabilityAiAssistant.disclaimer.title', {
|
||||
defaultMessage: 'Welcome to the Elastic AI Assistant for Observability',
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiText color="subdued" textAlign="center">
|
||||
{i18n.translate('xpack.observabilityAiAssistant.disclaimer.thisChatIsPoweredTextLabel', {
|
||||
defaultMessage:
|
||||
'This chat is powered by an integration with your LLM provider. LLMs are known to produce hallucinations. Elastic supports the configuration and connection to the LLM provider and to your Knowledge base, but is not responsible for the LLM responses.',
|
||||
})}
|
||||
</EuiText>
|
||||
</>
|
||||
<EuiText
|
||||
color="subdued"
|
||||
size="xs"
|
||||
textAlign="center"
|
||||
data-test-subj="observabilityAiAssistantDisclaimer"
|
||||
>
|
||||
{i18n.translate('xpack.observabilityAiAssistant.disclaimer.disclaimerLabel', {
|
||||
defaultMessage:
|
||||
"This chat is powered by an integration with your LLM provider. LLMs are known to sometimes present incorrect information as if it's correct. Elastic supports configuration and connection to the LLM provider and your knowledge base, but is not responsible for the LLM's responses.",
|
||||
})}
|
||||
</EuiText>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,175 +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, { useState } from 'react';
|
||||
import {
|
||||
EuiBetaBadge,
|
||||
EuiButton,
|
||||
EuiCard,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiIcon,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import { ConnectorSelectorBase } from '../connector_selector/connector_selector_base';
|
||||
import { Disclaimer } from './disclaimer';
|
||||
import { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base';
|
||||
import { StartedFrom } from '../../utils/get_timeline_items_from_conversation';
|
||||
import { useKibana } from '../../hooks/use_kibana';
|
||||
import type { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors';
|
||||
|
||||
export function InitialSetupPanel({
|
||||
connectors,
|
||||
startedFrom,
|
||||
}: {
|
||||
connectors: UseGenAIConnectorsResult;
|
||||
connectorsManagementHref: string;
|
||||
knowledgeBase: UseKnowledgeBaseResult;
|
||||
startedFrom?: StartedFrom;
|
||||
}) {
|
||||
const [connectorFlyoutOpen, setConnectorFlyoutOpen] = useState(false);
|
||||
|
||||
const {
|
||||
application: { navigateToApp, capabilities },
|
||||
triggersActionsUi: { getAddConnectorFlyout: ConnectorFlyout },
|
||||
} = useKibana().services;
|
||||
|
||||
const handleConnectorClick = () => {
|
||||
if (capabilities.management?.insightsAndAlerting?.triggersActions) {
|
||||
setConnectorFlyoutOpen(true);
|
||||
} else {
|
||||
navigateToApp('management', {
|
||||
path: '/insightsAndAlerting/triggersActionsConnectors/connectors',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onConnectorCreated = (createdConnector: ActionConnector) => {
|
||||
setConnectorFlyoutOpen(false);
|
||||
|
||||
if (createdConnector.actionTypeId === '.gen-ai') {
|
||||
connectors.reloadConnectors();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Disclaimer />
|
||||
|
||||
<EuiPanel paddingSize="m" style={{ overflowY: 'auto' }}>
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate('xpack.observabilityAiAssistant.initialSetupPanel.title', {
|
||||
defaultMessage: 'Start your Al experience with Elastic by completing the steps below.',
|
||||
})}
|
||||
</EuiText>
|
||||
|
||||
<EuiSpacer size="l" />
|
||||
|
||||
<EuiFlexGroup direction={startedFrom === 'conversationView' ? 'row' : 'column'}>
|
||||
<EuiFlexItem>
|
||||
<EuiCard
|
||||
icon={<EuiIcon type="devToolsApp" size="xl" />}
|
||||
title={i18n.translate(
|
||||
'xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.title',
|
||||
{
|
||||
defaultMessage: 'Connector setup',
|
||||
}
|
||||
)}
|
||||
description={
|
||||
!connectors.connectors?.length ? (
|
||||
<>
|
||||
<EuiText size="s">
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description1',
|
||||
{
|
||||
defaultMessage: 'Set up an OpenAI connector with your AI provider.',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
|
||||
<EuiText size="s">
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description2',
|
||||
{
|
||||
defaultMessage:
|
||||
'The OpenAI model needs to support function calls. We strongly recommend using GPT4.',
|
||||
}
|
||||
)}
|
||||
<EuiBetaBadge
|
||||
label=""
|
||||
css={{ boxShadow: 'none' }}
|
||||
tooltipContent={i18n.translate(
|
||||
'xpack.observabilityAiAssistant.technicalPreviewBadgeDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
"GPT4 is required for a more consistent experience when using function calls (for example when performing root cause analysis, visualizing data and more). GPT3.5 can work for some of the simpler workflows, such as explaining errors or for a ChatGPT like experience within Kibana which don't require the use of frequent function calls.",
|
||||
}
|
||||
)}
|
||||
iconType="iInCircle"
|
||||
size="s"
|
||||
/>
|
||||
</EuiText>
|
||||
</>
|
||||
) : connectors.connectors.length && !connectors.selectedConnector ? (
|
||||
<EuiText size="s">
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description',
|
||||
{
|
||||
defaultMessage: 'Please select a provider.',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
) : undefined
|
||||
}
|
||||
footer={
|
||||
!connectors.connectors?.length ? (
|
||||
<EuiButton
|
||||
data-test-subj="observabilityAiAssistantInitialSetupPanelSetUpGenerativeAiConnectorButton"
|
||||
fill
|
||||
color="primary"
|
||||
onClick={handleConnectorClick}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.buttonLabel',
|
||||
{
|
||||
defaultMessage: 'Set up OpenAI connector',
|
||||
}
|
||||
)}
|
||||
</EuiButton>
|
||||
) : connectors.connectors.length && !connectors.selectedConnector ? (
|
||||
<ConnectorSelectorBase {...connectors} />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="xxl" />
|
||||
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate('xpack.observabilityAiAssistant.initialSetupPanel.disclaimer', {
|
||||
defaultMessage:
|
||||
'The AI provider that is configured may collect telemetry when using the Elastic AI Assistant. Contact your AI provider for information on how data is collected.',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiPanel>
|
||||
|
||||
{connectorFlyoutOpen ? (
|
||||
<ConnectorFlyout
|
||||
onClose={() => setConnectorFlyoutOpen(false)}
|
||||
onConnectorCreated={onConnectorCreated}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { act, fireEvent, render as rtlRender } from '@testing-library/react';
|
||||
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
|
||||
import { waitForEuiPopoverClose, waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl';
|
||||
import { WelcomeMessage } from './welcome_message';
|
||||
import { useKibana } from '../../hooks/use_kibana';
|
||||
import type {
|
||||
MlDeploymentAllocationState,
|
||||
MlDeploymentState,
|
||||
} from '@elastic/elasticsearch/lib/api/types';
|
||||
|
||||
jest.mock('../../hooks/use_kibana');
|
||||
|
||||
const useKibanaMock = useKibana as jest.Mock;
|
||||
const navigateToApp = jest.fn();
|
||||
|
||||
const reloadConnectors = jest.fn();
|
||||
const selectConnector = jest.fn();
|
||||
const install = jest.fn().mockResolvedValue(undefined);
|
||||
const refresh = jest.fn();
|
||||
|
||||
const baseKnowledgeBase = {
|
||||
install,
|
||||
};
|
||||
|
||||
const emptyKnowledgeBase = {
|
||||
...baseKnowledgeBase,
|
||||
status: { value: { ready: false }, loading: false, refresh },
|
||||
isInstalling: false,
|
||||
installError: undefined,
|
||||
};
|
||||
|
||||
const installingKnowledgeBase = {
|
||||
...baseKnowledgeBase,
|
||||
status: { value: { ready: false }, loading: false, refresh },
|
||||
isInstalling: true,
|
||||
installError: undefined,
|
||||
};
|
||||
|
||||
const loadingKnowledgeBase = {
|
||||
...baseKnowledgeBase,
|
||||
status: { value: { ready: false }, loading: true, refresh },
|
||||
isInstalling: false,
|
||||
installError: undefined,
|
||||
};
|
||||
|
||||
const installedKnowledgeBase = {
|
||||
...baseKnowledgeBase,
|
||||
status: {
|
||||
value: {
|
||||
ready: true,
|
||||
model_name: 'foo',
|
||||
deployment_state: 'started' as MlDeploymentState,
|
||||
allocation_state: 'allocated' as MlDeploymentAllocationState,
|
||||
},
|
||||
loading: false,
|
||||
refresh,
|
||||
},
|
||||
isInstalling: false,
|
||||
installError: undefined,
|
||||
};
|
||||
|
||||
const baseConnectors = {
|
||||
reloadConnectors,
|
||||
selectConnector,
|
||||
loading: false,
|
||||
};
|
||||
|
||||
const emptyConnectors = {
|
||||
...baseConnectors,
|
||||
connectors: [],
|
||||
};
|
||||
|
||||
const filledConnectors = {
|
||||
...baseConnectors,
|
||||
connectors: [
|
||||
{
|
||||
id: 'test',
|
||||
actionTypeId: 'test',
|
||||
name: 'test',
|
||||
referencedByCount: 0,
|
||||
isPreconfigured: false,
|
||||
isDeprecated: false,
|
||||
isSystemAction: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const render = (component: React.ReactElement) => {
|
||||
return rtlRender(<IntlProvider locale="en">{component}</IntlProvider>);
|
||||
};
|
||||
|
||||
describe('Welcome Message', () => {
|
||||
beforeEach(() => {
|
||||
useKibanaMock.mockReturnValue({
|
||||
services: {
|
||||
application: { navigateToApp, capabilities: {} },
|
||||
http: { basePath: { prepend: jest.fn((path: string) => `/${path}`) } },
|
||||
triggersActionsUi: {
|
||||
getAddConnectorFlyout: () => <button data-test-subj="connectorFlyout">hello</button>,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe('when no connectors are available', () => {
|
||||
it('should show a disclaimer', () => {
|
||||
const { getByTestId } = render(
|
||||
<WelcomeMessage connectors={emptyConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
);
|
||||
|
||||
expect(getByTestId('observabilityAiAssistantDisclaimer')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show a set up connector button', () => {
|
||||
const { getByTestId } = render(
|
||||
<WelcomeMessage connectors={emptyConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
);
|
||||
|
||||
expect(
|
||||
getByTestId('observabilityAiAssistantInitialSetupPanelSetUpGenerativeAiConnectorButton')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('when no triggersactionsUi capabilities are available', () => {
|
||||
it('should navigate to stack management', () => {
|
||||
const { getByTestId } = render(
|
||||
<WelcomeMessage connectors={emptyConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
);
|
||||
|
||||
fireEvent.click(
|
||||
getByTestId('observabilityAiAssistantInitialSetupPanelSetUpGenerativeAiConnectorButton')
|
||||
);
|
||||
|
||||
expect(navigateToApp).toHaveBeenCalledWith('management', {
|
||||
path: '/insightsAndAlerting/triggersActionsConnectors/connectors',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when triggersactionsUi capabilities are available', () => {
|
||||
beforeEach(() => {
|
||||
useKibanaMock.mockReturnValue({
|
||||
services: {
|
||||
application: {
|
||||
navigateToApp,
|
||||
capabilities: {
|
||||
management: {
|
||||
insightsAndAlerting: {
|
||||
triggersActions: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
triggersActionsUi: {
|
||||
getAddConnectorFlyout: () => <button data-test-subj="connectorFlyout">hello</button>,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a connector flyout when clicking the set up connector button', () => {
|
||||
const { getByTestId } = render(
|
||||
<WelcomeMessage connectors={emptyConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
);
|
||||
|
||||
fireEvent.click(
|
||||
getByTestId('observabilityAiAssistantInitialSetupPanelSetUpGenerativeAiConnectorButton')
|
||||
);
|
||||
|
||||
expect(getByTestId('connectorFlyout')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('when creating a new connector', () => {
|
||||
// it('should call reloadConnectors and install knowledge base', () => {
|
||||
// const { getByTestId } = render(
|
||||
// <WelcomeMessage connectors={emptyConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
// );
|
||||
// fireEvent.click(
|
||||
// getByTestId('observabilityAiAssistantInitialSetupPanelSetUpGenerativeAiConnectorButton')
|
||||
// );
|
||||
// fireEvent.click(getByTestId('connectorFlyout'));
|
||||
// expect(reloadConnectors).toHaveBeenCalled();
|
||||
// expect(install).toHaveBeenCalled();
|
||||
// });
|
||||
// it('should install the knowledge base', () => {
|
||||
// const { getByTestId } = render(
|
||||
// <WelcomeMessage connectors={emptyConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
// );
|
||||
// fireEvent.click(
|
||||
// getByTestId('observabilityAiAssistantInitialSetupPanelSetUpGenerativeAiConnectorButton')
|
||||
// );
|
||||
// fireEvent.click(getByTestId('connectorFlyout'));
|
||||
// expect(install).toHaveBeenCalled();
|
||||
// });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when connectors are available', () => {
|
||||
it('should show a disclaimer', () => {
|
||||
const { getByTestId } = render(
|
||||
<WelcomeMessage connectors={filledConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
);
|
||||
|
||||
expect(getByTestId('observabilityAiAssistantDisclaimer')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('when knowledge base is not installed', () => {
|
||||
it('should render the retry and inspect errors buttons', () => {
|
||||
const { getByTestId } = render(
|
||||
<WelcomeMessage connectors={filledConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
);
|
||||
|
||||
expect(
|
||||
getByTestId('observabilityAiAssistantWelcomeMessageSetUpKnowledgeBaseButton')
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
getByTestId('observabilityAiAssistantWelcomeMessageInspectErrorsButton')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call kb install when clicking retry', async () => {
|
||||
const { getByTestId } = render(
|
||||
<WelcomeMessage connectors={filledConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(
|
||||
getByTestId('observabilityAiAssistantWelcomeMessageSetUpKnowledgeBaseButton')
|
||||
);
|
||||
expect(install).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a popover with installation errors when clicking inspect', async () => {
|
||||
const { getByTestId } = render(
|
||||
<WelcomeMessage connectors={filledConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
);
|
||||
|
||||
fireEvent.click(getByTestId('observabilityAiAssistantWelcomeMessageInspectErrorsButton'));
|
||||
|
||||
await waitForEuiPopoverOpen();
|
||||
|
||||
expect(
|
||||
getByTestId('observabilityAiAssistantWelcomeMessageKnowledgeBaseSetupErrorPanel')
|
||||
).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(
|
||||
getByTestId(
|
||||
'observabilityAiAssistantWelcomeMessageKnowledgeBaseSetupErrorPanelRetryInstallingLink'
|
||||
)
|
||||
);
|
||||
|
||||
await waitForEuiPopoverClose();
|
||||
|
||||
expect(install).toBeCalled();
|
||||
});
|
||||
|
||||
it('should navigate to ML when clicking the link in the error popover', async () => {
|
||||
const { getByTestId } = render(
|
||||
<WelcomeMessage connectors={filledConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
);
|
||||
|
||||
fireEvent.click(getByTestId('observabilityAiAssistantWelcomeMessageInspectErrorsButton'));
|
||||
|
||||
await waitForEuiPopoverOpen();
|
||||
|
||||
const link = getByTestId('observabilityAiAssistantWelcomeMessageTrainedModelsLink');
|
||||
|
||||
expect(link).toHaveProperty('href', 'http://app/ml/trained_models');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when knowledge base is installing', () => {
|
||||
it('should not show a failure message', () => {
|
||||
const { queryByTestId } = render(
|
||||
<WelcomeMessage connectors={filledConnectors} knowledgeBase={installingKnowledgeBase} />
|
||||
);
|
||||
|
||||
expect(
|
||||
queryByTestId('observabilityAiAssistantWelcomeMessageSetUpKnowledgeBaseButton')
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
queryByTestId('observabilityAiAssistantWelcomeMessageInspectErrorsButton')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show a disclaimer', () => {
|
||||
const { getByTestId } = render(
|
||||
<WelcomeMessage connectors={emptyConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
);
|
||||
|
||||
expect(getByTestId('observabilityAiAssistantDisclaimer')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when knowledge base is loading', () => {
|
||||
it('should not show a failure message', () => {
|
||||
const { queryByTestId } = render(
|
||||
<WelcomeMessage connectors={filledConnectors} knowledgeBase={loadingKnowledgeBase} />
|
||||
);
|
||||
|
||||
expect(
|
||||
queryByTestId('observabilityAiAssistantWelcomeMessageSetUpKnowledgeBaseButton')
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
queryByTestId('observabilityAiAssistantWelcomeMessageInspectErrorsButton')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show a disclaimer', () => {
|
||||
const { getByTestId } = render(
|
||||
<WelcomeMessage connectors={emptyConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
);
|
||||
|
||||
expect(getByTestId('observabilityAiAssistantDisclaimer')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when knowledge base is installed', () => {
|
||||
it('should not show a failure message', () => {
|
||||
const { queryByTestId } = render(
|
||||
<WelcomeMessage connectors={filledConnectors} knowledgeBase={installedKnowledgeBase} />
|
||||
);
|
||||
|
||||
expect(
|
||||
queryByTestId('observabilityAiAssistantWelcomeMessageSetUpKnowledgeBaseButton')
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
queryByTestId('observabilityAiAssistantWelcomeMessageInspectErrorsButton')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show a disclaimer', () => {
|
||||
const { getByTestId } = render(
|
||||
<WelcomeMessage connectors={emptyConnectors} knowledgeBase={emptyKnowledgeBase} />
|
||||
);
|
||||
|
||||
expect(getByTestId('observabilityAiAssistantDisclaimer')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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, { useState } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiImage,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
useCurrentEuiBreakpoint,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base';
|
||||
import type { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors';
|
||||
import ctaImage from '../../assets/elastic_ai_assistant.png';
|
||||
import { Disclaimer } from './disclaimer';
|
||||
import { WelcomeMessageConnectors } from './welcome_message_connectors';
|
||||
import { WelcomeMessageKnowledgeBase } from './welcome_message_knowledge_base';
|
||||
import { useKibana } from '../../hooks/use_kibana';
|
||||
|
||||
const fullHeightClassName = css`
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const centerMaxWidthClassName = css`
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
export function WelcomeMessage({
|
||||
connectors,
|
||||
knowledgeBase,
|
||||
}: {
|
||||
connectors: UseGenAIConnectorsResult;
|
||||
knowledgeBase: UseKnowledgeBaseResult;
|
||||
}) {
|
||||
const breakpoint = useCurrentEuiBreakpoint();
|
||||
|
||||
const {
|
||||
application: { navigateToApp, capabilities },
|
||||
triggersActionsUi: { getAddConnectorFlyout: ConnectorFlyout },
|
||||
} = useKibana().services;
|
||||
|
||||
const [connectorFlyoutOpen, setConnectorFlyoutOpen] = useState(false);
|
||||
|
||||
const handleConnectorClick = () => {
|
||||
if (capabilities.management?.insightsAndAlerting?.triggersActions) {
|
||||
setConnectorFlyoutOpen(true);
|
||||
} else {
|
||||
navigateToApp('management', {
|
||||
path: '/insightsAndAlerting/triggersActionsConnectors/connectors',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onConnectorCreated = (createdConnector: ActionConnector) => {
|
||||
setConnectorFlyoutOpen(false);
|
||||
|
||||
if (createdConnector.actionTypeId === '.gen-ai') {
|
||||
connectors.reloadConnectors();
|
||||
}
|
||||
|
||||
if (!knowledgeBase.status.value || knowledgeBase.status.value?.ready === false) {
|
||||
knowledgeBase.install();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
direction="column"
|
||||
gutterSize="none"
|
||||
className={fullHeightClassName}
|
||||
>
|
||||
<EuiFlexItem grow className={centerMaxWidthClassName}>
|
||||
<EuiSpacer size={breakpoint === 'xl' ? 'l' : 'l' ? 'l' : 's'} />
|
||||
|
||||
<EuiImage
|
||||
src={ctaImage}
|
||||
alt="Elastic AI Assistant"
|
||||
size={breakpoint === 'xl' ? 300 : 'm'}
|
||||
/>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiTitle size={breakpoint === 'xl' ? 'm' : 'l' ? 'm' : 's'}>
|
||||
<h2>
|
||||
{i18n.translate('xpack.observabilityAiAssistant.disclaimer.title', {
|
||||
defaultMessage: 'Welcome to the AI Assistant for Observability',
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<WelcomeMessageConnectors
|
||||
connectors={connectors}
|
||||
onSetupConnectorClick={handleConnectorClick}
|
||||
/>
|
||||
|
||||
<WelcomeMessageKnowledgeBase connectors={connectors} knowledgeBase={knowledgeBase} />
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiSpacer size="m" />
|
||||
<Disclaimer />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
{connectorFlyoutOpen ? (
|
||||
<ConnectorFlyout
|
||||
featureId="generativeAI"
|
||||
onConnectorCreated={onConnectorCreated}
|
||||
onClose={() => setConnectorFlyoutOpen(false)}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { css, keyframes } from '@emotion/css';
|
||||
import { EuiBetaBadge, EuiButton, EuiSpacer, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import type { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors';
|
||||
|
||||
const fadeInAnimation = keyframes`
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
const fadeInClassName = css`
|
||||
animation: ${fadeInAnimation} ${euiThemeVars.euiAnimSpeedNormal} ease-in-out;
|
||||
`;
|
||||
|
||||
export function WelcomeMessageConnectors({
|
||||
connectors,
|
||||
onSetupConnectorClick,
|
||||
}: {
|
||||
connectors: UseGenAIConnectorsResult;
|
||||
onSetupConnectorClick?: () => void;
|
||||
}) {
|
||||
return !connectors.loading && connectors.connectors?.length === 0 && onSetupConnectorClick ? (
|
||||
<div className={fadeInClassName}>
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description2',
|
||||
{
|
||||
defaultMessage:
|
||||
'Start working with the Elastic AI Assistant by setting up a connector for your AI provider. The OpenAI model needs to support function calls. We strongly recommend using GPT4.',
|
||||
}
|
||||
)}
|
||||
<EuiBetaBadge
|
||||
label=""
|
||||
css={{ boxShadow: 'none', inlineSize: 'unset', lineHeight: 'initial' }}
|
||||
tooltipContent={i18n.translate(
|
||||
'xpack.observabilityAiAssistant.technicalPreviewBadgeDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
"GPT4 is required for a more consistent experience when using function calls (for example when performing root cause analysis, visualizing data and more). GPT3.5 can work for some of the simpler workflows, such as explaining errors or for a ChatGPT like experience within Kibana which don't require the use of frequent function calls.",
|
||||
}
|
||||
)}
|
||||
iconType="iInCircle"
|
||||
size="s"
|
||||
/>
|
||||
</EuiText>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<div>
|
||||
<EuiButton
|
||||
data-test-subj="observabilityAiAssistantInitialSetupPanelSetUpGenerativeAiConnectorButton"
|
||||
fill
|
||||
color="primary"
|
||||
onClick={onSetupConnectorClick}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.buttonLabel',
|
||||
{
|
||||
defaultMessage: 'Set up GenAI connector',
|
||||
}
|
||||
)}
|
||||
</EuiButton>
|
||||
</div>
|
||||
</div>
|
||||
) : null;
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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, { useEffect, useState } from 'react';
|
||||
import { noop } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiIcon,
|
||||
EuiPopover,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import usePrevious from 'react-use/lib/usePrevious';
|
||||
import useTimeoutFn from 'react-use/lib/useTimeoutFn';
|
||||
import useInterval from 'react-use/lib/useInterval';
|
||||
import { WelcomeMessageKnowledgeBaseSetupErrorPanel } from './welcome_message_knowledge_base_setup_error_panel';
|
||||
import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base';
|
||||
import type { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors';
|
||||
|
||||
export function WelcomeMessageKnowledgeBase({
|
||||
connectors,
|
||||
knowledgeBase,
|
||||
}: {
|
||||
connectors: UseGenAIConnectorsResult;
|
||||
knowledgeBase: UseKnowledgeBaseResult;
|
||||
}) {
|
||||
const previouslyNotInstalled = usePrevious(knowledgeBase.status.value?.ready === false);
|
||||
const [showHasBeenInstalled, setShowHasBeenInstalled] = useState(false);
|
||||
const [timeoutTime, setTimeoutTime] = useState(0);
|
||||
const [, , reset] = useTimeoutFn(() => setShowHasBeenInstalled(false), timeoutTime);
|
||||
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||
const handleClosePopover = () => setIsPopoverOpen(false);
|
||||
|
||||
const [checkForInstallStatus, setCheckForInstallStatus] = useState(false);
|
||||
|
||||
// When the knowledge base is installed, show a success message for 3 seconds
|
||||
useEffect(() => {
|
||||
if (previouslyNotInstalled && knowledgeBase.status.value?.ready) {
|
||||
setTimeoutTime(3000);
|
||||
reset();
|
||||
setShowHasBeenInstalled(true);
|
||||
}
|
||||
}, [knowledgeBase.status.value?.ready, previouslyNotInstalled, reset]);
|
||||
|
||||
// When the knowledge base is installed, stop checking for install status
|
||||
useEffect(() => {
|
||||
if (!checkForInstallStatus && knowledgeBase.status.value?.ready) {
|
||||
setCheckForInstallStatus(false);
|
||||
}
|
||||
}, [checkForInstallStatus, knowledgeBase.status.value?.ready]);
|
||||
|
||||
// Check for install status every 5 seconds
|
||||
useInterval(
|
||||
() => {
|
||||
knowledgeBase.status.refresh();
|
||||
},
|
||||
checkForInstallStatus ? 5000 : null
|
||||
);
|
||||
|
||||
const handleRetryInstall = async () => {
|
||||
setCheckForInstallStatus(true);
|
||||
setIsPopoverOpen(false);
|
||||
|
||||
await knowledgeBase.install().then(() => {
|
||||
setCheckForInstallStatus(false);
|
||||
});
|
||||
};
|
||||
|
||||
return knowledgeBase.status.value?.ready !== undefined ? (
|
||||
<>
|
||||
{knowledgeBase.isInstalling ? (
|
||||
<>
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.welcomeMessage.weAreSettingUpTextLabel',
|
||||
{
|
||||
defaultMessage:
|
||||
'We are setting up your knowledge base. This may take a few minutes. You can continue to use the Assistant while this process is underway.',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="observabilityAiAssistantWelcomeMessageSettingUpKnowledgeBaseButton"
|
||||
isLoading
|
||||
onClick={noop}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.welcomeMessage.div.settingUpKnowledgeBaseLabel',
|
||||
{ defaultMessage: 'Setting up Knowledge base' }
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{connectors.connectors?.length ? (
|
||||
(!knowledgeBase.isInstalling && knowledgeBase.installError) ||
|
||||
(!knowledgeBase.isInstalling &&
|
||||
knowledgeBase.status.loading === false &&
|
||||
knowledgeBase.status.value.ready === false) ? (
|
||||
<>
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.welcomeMessageKnowledgeBase.yourKnowledgeBaseIsNotSetUpCorrectlyLabel',
|
||||
{ defaultMessage: 'Your Knowledge base is not set up correctly' }
|
||||
)}
|
||||
</EuiText>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup justifyContent="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<div>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="observabilityAiAssistantWelcomeMessageSetUpKnowledgeBaseButton"
|
||||
fill
|
||||
isLoading={checkForInstallStatus}
|
||||
iconType="refresh"
|
||||
onClick={handleRetryInstall}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.welcomeMessage.retryButtonLabel',
|
||||
{
|
||||
defaultMessage: 'Retry install',
|
||||
}
|
||||
)}
|
||||
</EuiButton>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiPopover
|
||||
button={
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="observabilityAiAssistantWelcomeMessageInspectErrorsButton"
|
||||
iconType="inspect"
|
||||
onClick={() => setIsPopoverOpen(!isPopoverOpen)}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.welcomeMessage.inspectErrorsButtonEmptyLabel',
|
||||
{ defaultMessage: 'Inspect issues' }
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
}
|
||||
isOpen={isPopoverOpen}
|
||||
panelPaddingSize="none"
|
||||
closePopover={handleClosePopover}
|
||||
>
|
||||
<WelcomeMessageKnowledgeBaseSetupErrorPanel
|
||||
knowledgeBase={knowledgeBase}
|
||||
onRetryInstall={handleRetryInstall}
|
||||
/>
|
||||
</EuiPopover>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
) : null
|
||||
) : null}
|
||||
|
||||
{showHasBeenInstalled ? (
|
||||
<div>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s" justifyContent="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="checkInCircleFilled" color="success" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.welcomeMessage.knowledgeBaseSuccessfullyInstalledLabel',
|
||||
{ defaultMessage: 'Knowledge base successfully installed' }
|
||||
)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
) : null;
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import {
|
||||
EuiCode,
|
||||
EuiDescriptionList,
|
||||
EuiDescriptionListTitle,
|
||||
EuiDescriptionListDescription,
|
||||
EuiLink,
|
||||
EuiSpacer,
|
||||
EuiIcon,
|
||||
EuiText,
|
||||
EuiHorizontalRule,
|
||||
EuiPanel,
|
||||
} from '@elastic/eui';
|
||||
import { css } from '@emotion/css';
|
||||
import { useKibana } from '../../hooks/use_kibana';
|
||||
import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base';
|
||||
|
||||
const panelContainerClassName = css`
|
||||
width: 330px;
|
||||
`;
|
||||
|
||||
export function WelcomeMessageKnowledgeBaseSetupErrorPanel({
|
||||
knowledgeBase,
|
||||
onRetryInstall,
|
||||
}: {
|
||||
knowledgeBase: UseKnowledgeBaseResult;
|
||||
onRetryInstall: () => void;
|
||||
}) {
|
||||
const { http } = useKibana().services;
|
||||
|
||||
const modelName = knowledgeBase.status.value?.model_name;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={panelContainerClassName}
|
||||
data-test-subj="observabilityAiAssistantWelcomeMessageKnowledgeBaseSetupErrorPanel"
|
||||
>
|
||||
<EuiPanel hasBorder={false} hasShadow={false} paddingSize="m">
|
||||
<EuiDescriptionList>
|
||||
<EuiDescriptionListTitle>
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.welcomeMessage.issuesDescriptionListTitleLabel',
|
||||
{ defaultMessage: 'Issues' }
|
||||
)}
|
||||
</EuiDescriptionListTitle>
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
<EuiDescriptionListDescription>
|
||||
<ul>
|
||||
{!knowledgeBase.status.value?.deployment_state ? (
|
||||
<li>
|
||||
<EuiIcon type="alert" color="subdued" />{' '}
|
||||
<FormattedMessage
|
||||
id="xpack.observabilityAiAssistant.welcomeMessage.modelIsNotDeployedLabel"
|
||||
defaultMessage="Model {modelName} is not deployed"
|
||||
values={{
|
||||
modelName: <EuiCode>{modelName}</EuiCode>,
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
) : null}
|
||||
|
||||
{knowledgeBase.status.value?.deployment_state &&
|
||||
knowledgeBase.status.value.deployment_state !== 'started' ? (
|
||||
<li>
|
||||
<EuiIcon type="alert" color="subdued" />{' '}
|
||||
<FormattedMessage
|
||||
id="xpack.observabilityAiAssistant.welcomeMessage.modelIsNotStartedLabel"
|
||||
defaultMessage="Deployment state of {modelName} is {deploymentState}"
|
||||
values={{
|
||||
modelName: <EuiCode>{modelName}</EuiCode>,
|
||||
deploymentState: (
|
||||
<EuiCode>{knowledgeBase.status.value?.deployment_state}</EuiCode>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
) : null}
|
||||
|
||||
{knowledgeBase.status.value?.allocation_state &&
|
||||
knowledgeBase.status.value.allocation_state !== 'fully_allocated' ? (
|
||||
<li>
|
||||
<EuiIcon type="alert" color="subdued" />{' '}
|
||||
<FormattedMessage
|
||||
id="xpack.observabilityAiAssistant.welcomeMessage.modelIsNotFullyAllocatedLabel"
|
||||
defaultMessage="Allocation state of {modelName} is {allocationState}"
|
||||
values={{
|
||||
modelName: <EuiCode>{modelName}</EuiCode>,
|
||||
allocationState: (
|
||||
<EuiCode>{knowledgeBase.status.value?.allocation_state}</EuiCode>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
</EuiDescriptionListDescription>
|
||||
</EuiDescriptionList>
|
||||
</EuiPanel>
|
||||
|
||||
<EuiHorizontalRule margin="none" />
|
||||
|
||||
<EuiPanel hasBorder={false} hasShadow={false} paddingSize="m">
|
||||
<EuiText color="subdued" size="xs">
|
||||
<FormattedMessage
|
||||
id="xpack.observabilityAiAssistant.welcomeMessage.div.checkTrainedModelsToLabel"
|
||||
defaultMessage="
|
||||
{retryInstallingLink} or check {trainedModelsLink} to ensure {modelName} is deployed and running."
|
||||
values={{
|
||||
modelName,
|
||||
retryInstallingLink: (
|
||||
<EuiLink
|
||||
data-test-subj="observabilityAiAssistantWelcomeMessageKnowledgeBaseSetupErrorPanelRetryInstallingLink"
|
||||
onClick={onRetryInstall}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.welcomeMessageKnowledgeBaseSetupErrorPanel.retryInstallingLinkLabel',
|
||||
{ defaultMessage: 'Retry install' }
|
||||
)}
|
||||
</EuiLink>
|
||||
),
|
||||
trainedModelsLink: (
|
||||
<EuiLink
|
||||
data-test-subj="observabilityAiAssistantWelcomeMessageTrainedModelsLink"
|
||||
external
|
||||
href={http.basePath.prepend('/app/ml/trained_models')}
|
||||
target="_blank"
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.observabilityAiAssistant.welcomeMessage.trainedModelsLinkLabel',
|
||||
{ defaultMessage: 'Trained Models' }
|
||||
)}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiPanel>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -5,6 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type {
|
||||
MlDeploymentAllocationState,
|
||||
MlDeploymentState,
|
||||
} from '@elastic/elasticsearch/lib/api/types';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { AbortableAsyncState, useAbortableAsync } from './use_abortable_async';
|
||||
import { useKibana } from './use_kibana';
|
||||
|
@ -14,8 +18,9 @@ export interface UseKnowledgeBaseResult {
|
|||
status: AbortableAsyncState<{
|
||||
ready: boolean;
|
||||
error?: any;
|
||||
deployment_state?: string;
|
||||
allocation_state?: string;
|
||||
deployment_state?: MlDeploymentState;
|
||||
allocation_state?: MlDeploymentAllocationState;
|
||||
model_name?: string;
|
||||
}>;
|
||||
isInstalling: boolean;
|
||||
installError?: Error;
|
||||
|
@ -50,15 +55,6 @@ export function useKnowledgeBase(): UseKnowledgeBaseResult {
|
|||
})
|
||||
.then(() => {
|
||||
status.refresh();
|
||||
toasts.addSuccess({
|
||||
title: i18n.translate('xpack.observabilityAiAssistant.knowledgeBaseReadyTitle', {
|
||||
defaultMessage: 'Knowledge base is ready',
|
||||
}),
|
||||
text: i18n.translate('xpack.observabilityAiAssistant.knowledgeBaseReadyContentReload', {
|
||||
defaultMessage: 'A page reload is needed to be able to use it.',
|
||||
}),
|
||||
toastLifeTimeMs: Number.MAX_VALUE,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
if (
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
MlDeploymentAllocationState,
|
||||
MlDeploymentState,
|
||||
} from '@elastic/elasticsearch/lib/api/types';
|
||||
import { notImplemented } from '@hapi/boom';
|
||||
import { nonEmptyStringRt, toBooleanRt } from '@kbn/io-ts-utils';
|
||||
import * as t from 'io-ts';
|
||||
|
@ -20,8 +25,9 @@ const getKnowledgeBaseStatus = createObservabilityAIAssistantServerRoute({
|
|||
): Promise<{
|
||||
ready: boolean;
|
||||
error?: any;
|
||||
deployment_state?: string;
|
||||
allocation_state?: string;
|
||||
deployment_state?: MlDeploymentState;
|
||||
allocation_state?: MlDeploymentAllocationState;
|
||||
model_name?: string;
|
||||
}> => {
|
||||
const client = await resources.service.getClient({ request: resources.request });
|
||||
|
||||
|
|
|
@ -275,15 +275,18 @@ export class KnowledgeBaseService {
|
|||
const elserModelStats = modelStats.trained_model_stats[0];
|
||||
const deploymentState = elserModelStats.deployment_stats?.state;
|
||||
const allocationState = elserModelStats.deployment_stats?.allocation_status.state;
|
||||
|
||||
return {
|
||||
ready: deploymentState === 'started' && allocationState === 'fully_allocated',
|
||||
deployment_state: deploymentState,
|
||||
allocation_state: allocationState,
|
||||
model_name: ELSER_MODEL_ID,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
error: error instanceof errors.ResponseError ? error.body.error : String(error),
|
||||
ready: false,
|
||||
model_name: ELSER_MODEL_ID,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -29629,7 +29629,6 @@
|
|||
"xpack.observabilityAiAssistant.chatTimeline.messages.elasticAssistant.label": "Assistant d'Elastic",
|
||||
"xpack.observabilityAiAssistant.chatTimeline.messages.system.label": "Système",
|
||||
"xpack.observabilityAiAssistant.chatTimeline.messages.user.label": "Vous",
|
||||
"xpack.observabilityAiAssistant.chatWelcomePanel.knowledgeBase.buttonLabel.notInstalledYet": "Configurer la base de connaissances",
|
||||
"xpack.observabilityAiAssistant.checkingKbAvailability": "Vérification de la disponibilité de la base de connaissances",
|
||||
"xpack.observabilityAiAssistant.confirmDeleteButtonText": "Supprimer la conversation",
|
||||
"xpack.observabilityAiAssistant.confirmDeleteConversationContent": "Cette action ne peut pas être annulée.",
|
||||
|
@ -29662,13 +29661,8 @@
|
|||
"xpack.observabilityAiAssistant.incorrectLicense.manageLicense": "Gérer la licence",
|
||||
"xpack.observabilityAiAssistant.incorrectLicense.subscriptionPlansButton": "Plans d'abonnement",
|
||||
"xpack.observabilityAiAssistant.incorrectLicense.title": "Mettez votre licence à niveau",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.disclaimer": "Le fournisseur d'intelligence artificielle configuré peut collecter des données télémétriques lors de l'utilisation de l'assistant d'intelligence artificielle d'Elastic. Contactez votre fournisseur d'intelligence artificielle pour obtenir des informations sur le mode de collecte des données.",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.buttonLabel": "Configurez un connecteur OpenAI",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description": "Veuillez sélectionner un fournisseur.",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description1": "Configurez un connecteur OpenAI avec votre fournisseur d'intelligence artificielle.",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description2": "Le modèle OpenAi doit être compatible avec l'appel de fonctions. Nous recommandons fortement d'utiliser GPT4.",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.title": "Configuration du connecteur",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.title": "Embarquez aux côtés de l'intelligence artificielle grâce à Elastic une fois les étapes ci-dessous effectuées.",
|
||||
"xpack.observabilityAiAssistant.insight.actions": "Actions",
|
||||
"xpack.observabilityAiAssistant.insight.defaultDescription": "Obtenez des informations utiles grâce à l'assistant d'intelligence artificielle d'Elastic.",
|
||||
"xpack.observabilityAiAssistant.insight.error.buttonLabel": "Régénérer",
|
||||
|
@ -29679,8 +29673,6 @@
|
|||
"xpack.observabilityAiAssistant.insight.feedbackButtons.title": "Cela a-t-il été utile ?",
|
||||
"xpack.observabilityAiAssistant.insight.response.startChat": "Lancer le chat",
|
||||
"xpack.observabilityAiAssistant.installingKb": "Configuration de la base de connaissances",
|
||||
"xpack.observabilityAiAssistant.knowledgeBaseReadyContentReload": "Il faut recharger la page pour pouvoir l'utiliser.",
|
||||
"xpack.observabilityAiAssistant.knowledgeBaseReadyTitle": "La base de connaissances est prête",
|
||||
"xpack.observabilityAiAssistant.lensFunction.openInLens": "Ouvrir dans Lens",
|
||||
"xpack.observabilityAiAssistant.lensFunction.save": "Enregistrer",
|
||||
"xpack.observabilityAiAssistant.missingCredentialsCallout.buttonLabel": "Connecter l'assistant",
|
||||
|
|
|
@ -29629,7 +29629,6 @@
|
|||
"xpack.observabilityAiAssistant.chatTimeline.messages.elasticAssistant.label": "Elastic Assistant",
|
||||
"xpack.observabilityAiAssistant.chatTimeline.messages.system.label": "システム",
|
||||
"xpack.observabilityAiAssistant.chatTimeline.messages.user.label": "あなた",
|
||||
"xpack.observabilityAiAssistant.chatWelcomePanel.knowledgeBase.buttonLabel.notInstalledYet": "ナレッジベースをセットアップ",
|
||||
"xpack.observabilityAiAssistant.checkingKbAvailability": "ナレッジベースの利用可能性を確認中",
|
||||
"xpack.observabilityAiAssistant.confirmDeleteButtonText": "会話を削除",
|
||||
"xpack.observabilityAiAssistant.confirmDeleteConversationContent": "この操作は元に戻すことができません。",
|
||||
|
@ -29662,13 +29661,8 @@
|
|||
"xpack.observabilityAiAssistant.incorrectLicense.manageLicense": "ライセンスの管理",
|
||||
"xpack.observabilityAiAssistant.incorrectLicense.subscriptionPlansButton": "サブスクリプションオプション",
|
||||
"xpack.observabilityAiAssistant.incorrectLicense.title": "ライセンスをアップグレード",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.disclaimer": "構成されたAIプロバイダーは、Elastic AI Assistantの使用時にテレメトリを収集することがあります。データの収集方法については、AIプロバイダーにお問い合わせください。",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.buttonLabel": "OpenAIコネクターをセットアップ",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description": "プロバイダーを選択してください。",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description1": "AIプロバイダーとOpenAIコネクターを設定します。",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description2": "OpenAIモデルは関数呼び出しをサポートしている必要があります。GPT4の使用を強くお勧めします。",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.title": "コネクターセットアップ",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.title": "以下のステップを完了することで、ElasticでAIエクスペリエンスを開始できます。",
|
||||
"xpack.observabilityAiAssistant.insight.actions": "アクション",
|
||||
"xpack.observabilityAiAssistant.insight.defaultDescription": "Elastic AI Assistantから役立つインサイトを得ることができます。",
|
||||
"xpack.observabilityAiAssistant.insight.error.buttonLabel": "再生成",
|
||||
|
@ -29679,8 +29673,6 @@
|
|||
"xpack.observabilityAiAssistant.insight.feedbackButtons.title": "役に立ちましたか。",
|
||||
"xpack.observabilityAiAssistant.insight.response.startChat": "チャットを開始",
|
||||
"xpack.observabilityAiAssistant.installingKb": "ナレッジベースをセットアップ中",
|
||||
"xpack.observabilityAiAssistant.knowledgeBaseReadyContentReload": "使用するにはページの再読み込みが必要です。",
|
||||
"xpack.observabilityAiAssistant.knowledgeBaseReadyTitle": "ナレッジベースを使用できます",
|
||||
"xpack.observabilityAiAssistant.lensFunction.openInLens": "Lensで開く",
|
||||
"xpack.observabilityAiAssistant.lensFunction.save": "保存",
|
||||
"xpack.observabilityAiAssistant.missingCredentialsCallout.buttonLabel": "アシスタントを接続",
|
||||
|
|
|
@ -29626,7 +29626,6 @@
|
|||
"xpack.observabilityAiAssistant.chatTimeline.messages.elasticAssistant.label": "Elastic 助手",
|
||||
"xpack.observabilityAiAssistant.chatTimeline.messages.system.label": "系统",
|
||||
"xpack.observabilityAiAssistant.chatTimeline.messages.user.label": "您",
|
||||
"xpack.observabilityAiAssistant.chatWelcomePanel.knowledgeBase.buttonLabel.notInstalledYet": "设置知识库",
|
||||
"xpack.observabilityAiAssistant.checkingKbAvailability": "正在检查知识库的可用性",
|
||||
"xpack.observabilityAiAssistant.confirmDeleteButtonText": "删除对话",
|
||||
"xpack.observabilityAiAssistant.confirmDeleteConversationContent": "此操作无法撤消。",
|
||||
|
@ -29659,13 +29658,8 @@
|
|||
"xpack.observabilityAiAssistant.incorrectLicense.manageLicense": "管理许可证",
|
||||
"xpack.observabilityAiAssistant.incorrectLicense.subscriptionPlansButton": "订阅计划",
|
||||
"xpack.observabilityAiAssistant.incorrectLicense.title": "升级您的许可证",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.disclaimer": "配置的 AI 提供商可能会在使用 Elastic AI 助手时收集遥测数据。请联系您的 AI 提供商了解有关如何收集数据的信息。",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.buttonLabel": "设置 OpenAI 连接器",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description": "请选择提供商。",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description1": "通过 AI 提供商设置 OpenAI 连接器。",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.description2": "OpenAI 模型需要支持函数调用。强烈建议使用 GPT4。",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.setupConnector.title": "连接器设置",
|
||||
"xpack.observabilityAiAssistant.initialSetupPanel.title": "通过完成以下步骤,利用 Elastic 开始您的 AI 体验。",
|
||||
"xpack.observabilityAiAssistant.insight.actions": "操作",
|
||||
"xpack.observabilityAiAssistant.insight.defaultDescription": "从 Elastic AI 助手获取有用的洞见。",
|
||||
"xpack.observabilityAiAssistant.insight.error.buttonLabel": "重新生成",
|
||||
|
@ -29676,8 +29670,6 @@
|
|||
"xpack.observabilityAiAssistant.insight.feedbackButtons.title": "这是否有帮助?",
|
||||
"xpack.observabilityAiAssistant.insight.response.startChat": "开始聊天",
|
||||
"xpack.observabilityAiAssistant.installingKb": "正在设置知识库",
|
||||
"xpack.observabilityAiAssistant.knowledgeBaseReadyContentReload": "需要重新加载页面才能使用它。",
|
||||
"xpack.observabilityAiAssistant.knowledgeBaseReadyTitle": "知识库已准备就绪",
|
||||
"xpack.observabilityAiAssistant.lensFunction.openInLens": "在 Lens 中打开",
|
||||
"xpack.observabilityAiAssistant.lensFunction.save": "保存",
|
||||
"xpack.observabilityAiAssistant.missingCredentialsCallout.buttonLabel": "连接助手",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue