mirror of
https://github.com/elastic/kibana.git
synced 2025-04-18 23:21:39 -04:00
[Playground chat] UX cleanup for EIS on by default (#217410)
## Summary This PR involves changes in the UX for playground setup page and Palyground Chat. Following items have been addressed. - [x] Convert LLM Connected button to a label that is not interactive - [x] Rename that label to "Elastic LLM Connected" if EIS is connected, otherwise "LLM Connected" - [x] Split the main panel into two panel: one for connecting to an LLM, one for adding data - [x] Add unit tests # Before   # After   ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Identify risks Does this PR introduce any risks? For example, consider risks like hard to test bugs, performance regression, potential of data loss. Describe the risk, its severity, and mitigation for each identified risk. Invite stakeholders and evaluate how to proceed before merging. - [ ] [See some risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) - [ ] ... --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Liam Thompson <32779855+leemthompo@users.noreply.github.com>
This commit is contained in:
parent
9ee5741134
commit
398123d22c
18 changed files with 396 additions and 133 deletions
|
@ -34378,7 +34378,6 @@
|
|||
"xpack.searchPlayground.setupPage.descriptionLLM": "Pour commencer, connectez-vous à votre fournisseur LLM et sélectionnez vos sources de données.",
|
||||
"xpack.searchPlayground.setupPage.documentationLink": "Lire la documentation",
|
||||
"xpack.searchPlayground.setupPage.learnMore": "Envie d'en savoir plus ?",
|
||||
"xpack.searchPlayground.setupPage.llmConnectedButtonLabel": "LLM connecté",
|
||||
"xpack.searchPlayground.setupPage.queryBuilder.title": "Ajouter des données à cette requête",
|
||||
"xpack.searchPlayground.setupPage.title": "Configurer une expérience de chat",
|
||||
"xpack.searchPlayground.setupPage.uploadFileLabel": "Charger le fichier",
|
||||
|
@ -34390,7 +34389,6 @@
|
|||
"xpack.searchPlayground.sidebar.instructionsField.placeholder": "Me remplacer",
|
||||
"xpack.searchPlayground.sidebar.summarizationModel.label": "Modèle",
|
||||
"xpack.searchPlayground.sidebar.summarizationModel.manageConnectorLink": "Gérer le connecteur",
|
||||
"xpack.searchPlayground.sidebar.summarizationModel.manageConnectorTooltip": "Gérer",
|
||||
"xpack.searchPlayground.sidebar.summarizationTitle": "Paramètres du modèle",
|
||||
"xpack.searchPlayground.sources.indices.label": "Index sélectionnés",
|
||||
"xpack.searchPlayground.sources.indices.removeIndex": "Retirer l'index des sources",
|
||||
|
|
|
@ -34356,7 +34356,6 @@
|
|||
"xpack.searchPlayground.setupPage.descriptionLLM": "開始するには、LLMプロバイダーに接続し、データソースを選択してください。",
|
||||
"xpack.searchPlayground.setupPage.documentationLink": "ドキュメンテーションを表示",
|
||||
"xpack.searchPlayground.setupPage.learnMore": "詳細について",
|
||||
"xpack.searchPlayground.setupPage.llmConnectedButtonLabel": "LLM未接続",
|
||||
"xpack.searchPlayground.setupPage.queryBuilder.title": "データをクエリに追加",
|
||||
"xpack.searchPlayground.setupPage.title": "チャット体験を設定",
|
||||
"xpack.searchPlayground.setupPage.uploadFileLabel": "ファイルをアップロード",
|
||||
|
@ -34367,7 +34366,6 @@
|
|||
"xpack.searchPlayground.sidebar.instructionsField.placeholder": "置換",
|
||||
"xpack.searchPlayground.sidebar.summarizationModel.label": "モデル",
|
||||
"xpack.searchPlayground.sidebar.summarizationModel.manageConnectorLink": "コネクターを管理",
|
||||
"xpack.searchPlayground.sidebar.summarizationModel.manageConnectorTooltip": "管理",
|
||||
"xpack.searchPlayground.sidebar.summarizationTitle": "モデル設定",
|
||||
"xpack.searchPlayground.sources.indices.label": "選択したインデックス",
|
||||
"xpack.searchPlayground.sources.indices.removeIndex": "ソースからインデックスを削除",
|
||||
|
|
|
@ -34414,7 +34414,6 @@
|
|||
"xpack.searchPlayground.setupPage.descriptionLLM": "连接到 LLM 提供商并选择数据以开始。",
|
||||
"xpack.searchPlayground.setupPage.documentationLink": "阅读文档",
|
||||
"xpack.searchPlayground.setupPage.learnMore": "希望了解详情?",
|
||||
"xpack.searchPlayground.setupPage.llmConnectedButtonLabel": "已连接 LLM",
|
||||
"xpack.searchPlayground.setupPage.queryBuilder.title": "将数据添加到查询",
|
||||
"xpack.searchPlayground.setupPage.title": "设置聊天体验",
|
||||
"xpack.searchPlayground.setupPage.uploadFileLabel": "上传文件",
|
||||
|
@ -34426,7 +34425,6 @@
|
|||
"xpack.searchPlayground.sidebar.instructionsField.placeholder": "替换我",
|
||||
"xpack.searchPlayground.sidebar.summarizationModel.label": "模型",
|
||||
"xpack.searchPlayground.sidebar.summarizationModel.manageConnectorLink": "管理连接器",
|
||||
"xpack.searchPlayground.sidebar.summarizationModel.manageConnectorTooltip": "管理",
|
||||
"xpack.searchPlayground.sidebar.summarizationTitle": "模型设置",
|
||||
"xpack.searchPlayground.sources.indices.label": "选定索引",
|
||||
"xpack.searchPlayground.sources.indices.removeIndex": "从源中移除索引",
|
||||
|
|
|
@ -5,60 +5,26 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiLink,
|
||||
EuiTitle,
|
||||
useEuiTheme,
|
||||
} from '@elastic/eui';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiTitle, useEuiTheme } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useWatch } from 'react-hook-form';
|
||||
import { docLinks } from '../../common/doc_links';
|
||||
import { EditContextPanel } from './edit_context/edit_context_panel';
|
||||
import { PlaygroundForm, PlaygroundFormFields } from '../types';
|
||||
import { useManagementLink } from '../hooks/use_management_link';
|
||||
import { SummarizationPanel } from './summarization_panel/summarization_panel';
|
||||
|
||||
export const ChatSidebar: React.FC = () => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const selectedModel = useWatch<PlaygroundForm, PlaygroundFormFields.summarizationModel>({
|
||||
name: PlaygroundFormFields.summarizationModel,
|
||||
});
|
||||
const managementLink = useManagementLink(selectedModel?.connectorId);
|
||||
const panels = [
|
||||
{
|
||||
title: i18n.translate('xpack.searchPlayground.sidebar.summarizationTitle', {
|
||||
defaultMessage: 'Model settings',
|
||||
defaultMessage: 'LLM settings',
|
||||
}),
|
||||
children: <SummarizationPanel />,
|
||||
extraAction: (
|
||||
<EuiButtonEmpty
|
||||
target="_blank"
|
||||
href={managementLink}
|
||||
data-test-subj="manageConnectorsLink"
|
||||
iconType="wrench"
|
||||
size="s"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.searchPlayground.sidebar.summarizationModel.manageConnectorLink',
|
||||
{
|
||||
defaultMessage: 'Manage connector',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.sidebar.summarizationModel.manageConnectorTooltip"
|
||||
defaultMessage="Manage"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.searchPlayground.sidebar.contextTitle', {
|
||||
defaultMessage: 'Context',
|
||||
defaultMessage: 'Playground context',
|
||||
}),
|
||||
extraAction: (
|
||||
<EuiLink
|
||||
|
|
|
@ -66,6 +66,18 @@ describe('EditContextFlyout component tests', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should render the documentSizeButtonGroup with all options', () => {
|
||||
// Check if the EuiButtonGroup is rendered
|
||||
const buttonGroup = screen.getByTestId('documentSizeButtonGroup');
|
||||
expect(buttonGroup).toBeInTheDocument();
|
||||
|
||||
// Check if all options are rendered within the button group
|
||||
expect(screen.getByTestId('playground_context_doc_number-1')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('playground_context_doc_number-3')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('playground_context_doc_number-5')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('playground_context_doc_number-10')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should see the context fields', async () => {
|
||||
expect(screen.getByTestId('contextFieldsSelectable-index1')).toBeInTheDocument();
|
||||
const listButton = screen
|
||||
|
|
|
@ -5,8 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiPanel, EuiSelect, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFormRow,
|
||||
EuiPanel,
|
||||
EuiText,
|
||||
EuiButtonGroup,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useController } from 'react-hook-form';
|
||||
|
@ -17,6 +23,7 @@ import { AnalyticsEvents } from '../../analytics/constants';
|
|||
import { ContextFieldsSelect } from './context_fields_select';
|
||||
|
||||
export const EditContextPanel: React.FC = () => {
|
||||
const idPrefix = 'playground_context_doc_number';
|
||||
const usageTracker = useUsageTracker();
|
||||
const { fields } = useSourceIndicesFields();
|
||||
|
||||
|
@ -43,46 +50,75 @@ export const EditContextPanel: React.FC = () => {
|
|||
[onChangeSourceFields, sourceFields, usageTracker]
|
||||
);
|
||||
|
||||
const handleDocSizeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const handleDocSizeButtonGroupChange = (value: number) => {
|
||||
usageTracker?.click(AnalyticsEvents.editContextDocSizeChanged);
|
||||
onChangeSize(Number(e.target.value));
|
||||
onChangeSize(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiPanel data-test-subj="editContextPanel">
|
||||
<EuiFlexGroup direction="column" gutterSize="l">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.searchPlayground.editContext.docsRetrievedCount', {
|
||||
defaultMessage: 'Number of documents sent to LLM',
|
||||
})}
|
||||
fullWidth
|
||||
>
|
||||
<EuiSelect
|
||||
data-test-subj="contextPanelDocumentNumberSelect"
|
||||
options={[
|
||||
{
|
||||
value: 1,
|
||||
text: '1',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
text: '3',
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
text: '5',
|
||||
},
|
||||
{
|
||||
value: 10,
|
||||
text: '10',
|
||||
},
|
||||
]}
|
||||
value={docSize}
|
||||
onChange={handleDocSizeChange}
|
||||
fullWidth
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFlexGroup direction="column" gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<EuiText>
|
||||
<h5>
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.documentsSize.table.title"
|
||||
defaultMessage="Documents"
|
||||
/>
|
||||
</h5>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexGroup direction="row" gutterSize="m" alignItems="center">
|
||||
<EuiFlexItem>
|
||||
<EuiText size="xs">
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.editContext.docsRetrievedCount"
|
||||
defaultMessage="Number of documents sent"
|
||||
/>
|
||||
</strong>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiButtonGroup
|
||||
data-test-subj="documentSizeButtonGroup"
|
||||
legend="Number of documents sent"
|
||||
isFullWidth={true}
|
||||
buttonSize="compressed"
|
||||
options={[
|
||||
{
|
||||
id: `${idPrefix}-1`,
|
||||
label: '1',
|
||||
value: 1,
|
||||
'data-test-subj': `${idPrefix}-1`,
|
||||
},
|
||||
{
|
||||
id: `${idPrefix}-3`,
|
||||
label: '3',
|
||||
value: 3,
|
||||
'data-test-subj': `${idPrefix}-3`,
|
||||
},
|
||||
{
|
||||
id: `${idPrefix}-5`,
|
||||
label: '5',
|
||||
value: 5,
|
||||
'data-test-subj': `${idPrefix}-5`,
|
||||
},
|
||||
{
|
||||
id: `${idPrefix}-10`,
|
||||
label: '10',
|
||||
value: 10,
|
||||
'data-test-subj': `${idPrefix}-10`,
|
||||
},
|
||||
]}
|
||||
idSelected={`${idPrefix}-${docSize}`}
|
||||
onChange={(_, value) => handleDocSizeButtonGroupChange(value)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup direction="column" gutterSize="m">
|
||||
|
|
|
@ -37,7 +37,6 @@ export const AddDataSources: React.FC = () => {
|
|||
</EuiButtonEmpty>
|
||||
) : (
|
||||
<EuiButton
|
||||
fill
|
||||
iconType="plusInCircle"
|
||||
onClick={() => setShowFlyout(true)}
|
||||
data-test-subj="addDataSourcesButton"
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
EuiLink,
|
||||
EuiLoadingSpinner,
|
||||
EuiTitle,
|
||||
EuiCard,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
|
@ -70,14 +71,52 @@ export const ChatSetupPage: React.FC = () => {
|
|||
<EuiLoadingSpinner />
|
||||
) : (
|
||||
<>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ConnectLLMButton />
|
||||
<EuiFlexItem style={{ minWidth: 360 }}>
|
||||
<EuiCard
|
||||
textAlign="left"
|
||||
titleSize="xs"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.setupPage.connectToLLM"
|
||||
defaultMessage="Large Language Model (LLM)"
|
||||
/>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.setupPage.connectToLLMDescription"
|
||||
defaultMessage="Select a model to integrate with your chat experience. You can also set up your own connection."
|
||||
/>
|
||||
}
|
||||
footer={<ConnectLLMButton />}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
{indices.length ? <AddDataSources /> : <CreateIndexButton />}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<UploadFileButton isSetup={true} />
|
||||
<EuiFlexItem style={{ minWidth: 360 }}>
|
||||
<EuiCard
|
||||
textAlign="left"
|
||||
titleSize="xs"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.setupPage.elasticsearchData"
|
||||
defaultMessage="Elasticsearch Data"
|
||||
/>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.setupPage.elasticsearchDataDescription"
|
||||
defaultMessage="Select your data sources to include as context or upload files to start."
|
||||
/>
|
||||
}
|
||||
footer={
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
{indices.length ? <AddDataSources /> : <CreateIndexButton />}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<UploadFileButton isSetup={true} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { ConnectLLMButton } from './connect_llm_button';
|
|||
import { useKibana } from '../../hooks/use_kibana';
|
||||
import { useLoadConnectors } from '../../hooks/use_load_connectors';
|
||||
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
|
||||
import { LLMs } from '../../../common/types';
|
||||
|
||||
const render = (children: React.ReactNode) =>
|
||||
testingLibraryRender(<IntlProvider locale="en">{children}</IntlProvider>);
|
||||
|
@ -30,6 +31,12 @@ const mockConnectors = {
|
|||
'2': { title: 'Connector 2' },
|
||||
};
|
||||
|
||||
const mockEisConnectors = {
|
||||
id: 'connectorId4',
|
||||
name: 'Elastic Managed LLM',
|
||||
type: LLMs.inference,
|
||||
};
|
||||
|
||||
describe('ConnectLLMButton', () => {
|
||||
beforeEach(() => {
|
||||
(useKibana as jest.Mock).mockReturnValue({
|
||||
|
@ -59,8 +66,9 @@ describe('ConnectLLMButton', () => {
|
|||
isLoading: false,
|
||||
isSuccess: true,
|
||||
});
|
||||
const { getByTestId } = render(<ConnectLLMButton />);
|
||||
const { getByTestId, getByText } = render(<ConnectLLMButton />);
|
||||
expect(getByTestId('connectLLMButton')).toBeInTheDocument();
|
||||
expect(getByText('Connect to an LLM')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('show the flyout when the button is clicked', async () => {
|
||||
|
@ -77,14 +85,51 @@ describe('ConnectLLMButton', () => {
|
|||
await waitFor(() => expect(getByTestId('addConnectorFlyout')).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it('show success button when connector exists', async () => {
|
||||
it('show the flyout when manageConnectorsLink is clicked', async () => {
|
||||
(useLoadConnectors as jest.Mock).mockReturnValue({
|
||||
data: [{}],
|
||||
data: [
|
||||
{
|
||||
name: 'conn-1',
|
||||
type: LLMs.openai,
|
||||
},
|
||||
],
|
||||
isLoading: false,
|
||||
isSuccess: true,
|
||||
});
|
||||
const { queryByTestId } = render(<ConnectLLMButton />);
|
||||
const { getByTestId, queryByTestId } = render(<ConnectLLMButton />);
|
||||
|
||||
expect(queryByTestId('successConnectLLMButton')).toBeInTheDocument();
|
||||
expect(queryByTestId('addConnectorFlyout')).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(getByTestId('manageConnectorsLink'));
|
||||
await waitFor(() => expect(getByTestId('addConnectorFlyout')).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it('show success text when connector exists', async () => {
|
||||
(useLoadConnectors as jest.Mock).mockReturnValue({
|
||||
data: [
|
||||
{
|
||||
name: 'conn-1',
|
||||
type: LLMs.openai,
|
||||
},
|
||||
],
|
||||
isLoading: false,
|
||||
isSuccess: true,
|
||||
});
|
||||
const { queryByTestId, getByText } = render(<ConnectLLMButton />);
|
||||
|
||||
expect(queryByTestId('successConnectLLMText')).toBeInTheDocument();
|
||||
expect(getByText('conn-1 connected')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('show success text when EIS connector exists', async () => {
|
||||
(useLoadConnectors as jest.Mock).mockReturnValue({
|
||||
data: [mockEisConnectors],
|
||||
isLoading: false,
|
||||
isSuccess: true,
|
||||
});
|
||||
const { queryByTestId, getByText } = render(<ConnectLLMButton />);
|
||||
|
||||
expect(queryByTestId('successConnectLLMText')).toBeInTheDocument();
|
||||
expect(getByText('Elastic Managed LLM connected')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,14 +5,23 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiButton, EuiButtonEmpty } from '@elastic/eui';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiText,
|
||||
EuiIcon,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiButtonEmpty,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { GenerativeAIForSearchPlaygroundConnectorFeatureId } from '@kbn/actions-plugin/common';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useKibana } from '../../hooks/use_kibana';
|
||||
import { useLoadConnectors } from '../../hooks/use_load_connectors';
|
||||
import { useUsageTracker } from '../../hooks/use_usage_tracker';
|
||||
import { AnalyticsEvents } from '../../analytics/constants';
|
||||
import { LLMs } from '../../../common/types';
|
||||
|
||||
export const ConnectLLMButton: React.FC = () => {
|
||||
const [connectorFlyoutOpen, setConnectorFlyoutOpen] = useState(false);
|
||||
|
@ -49,21 +58,47 @@ export const ConnectLLMButton: React.FC = () => {
|
|||
return (
|
||||
<>
|
||||
{connectors?.length ? (
|
||||
<EuiButtonEmpty
|
||||
iconType="check"
|
||||
color="success"
|
||||
onClick={handleSetupGenAiConnector}
|
||||
data-test-subj="successConnectLLMButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.setupPage.llmConnectedButtonLabel"
|
||||
defaultMessage="LLM connected"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s" data-test-subj="successConnectLLMText">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="checkInCircleFilled" color="success" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="success">
|
||||
{connectors.some((connector) => connector.type === LLMs.inference) ? (
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.setupPage.elasticManagedLlmConnectedButtonLabel"
|
||||
defaultMessage="{connectorName} connected"
|
||||
values={{
|
||||
connectorName:
|
||||
connectors.filter((connector) => connector.type === LLMs.inference)[0]
|
||||
?.name || 'Elastic Managed LLM',
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.setupPage.llmConnectedButtonLabel"
|
||||
defaultMessage="{connectorName} connected"
|
||||
values={{ connectorName: connectors[0]?.name || 'LLM' }}
|
||||
/>
|
||||
)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
target="_blank"
|
||||
data-test-subj="manageConnectorsLink"
|
||||
iconType="wrench"
|
||||
size="s"
|
||||
onClick={handleSetupGenAiConnector}
|
||||
aria-label={i18n.translate('xpack.searchPlayground.setupPage.manageConnectorLink', {
|
||||
defaultMessage: 'Manage connector',
|
||||
})}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<EuiButton
|
||||
fill
|
||||
iconType="link"
|
||||
iconType="sparkles"
|
||||
data-test-subj="connectLLMButton"
|
||||
onClick={handleSetupGenAiConnector}
|
||||
>
|
||||
|
|
|
@ -37,9 +37,7 @@ export const CreateIndexButton: React.FC = () => {
|
|||
return createIndexUrl ? (
|
||||
// eslint-disable-next-line @elastic/eui/href-or-on-click
|
||||
<EuiButton
|
||||
color="primary"
|
||||
iconType="plusInCircle"
|
||||
fill
|
||||
data-test-subj="createIndexButton"
|
||||
href={createIndexUrl}
|
||||
onClick={handleCreateIndexClick}
|
||||
|
|
|
@ -60,6 +60,7 @@ describe('SummarizationModel', () => {
|
|||
<SummarizationModel selectedModel={models[1]} models={models} onSelect={jest.fn()} />
|
||||
);
|
||||
|
||||
expect(getByTestId('aiConnectorTitle')).toBeInTheDocument();
|
||||
expect(getByTestId('summarizationModelSelect')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,14 +13,17 @@ import {
|
|||
EuiFormRow,
|
||||
EuiIcon,
|
||||
EuiSuperSelect,
|
||||
EuiButtonEmpty,
|
||||
type EuiSuperSelectOption,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { AnalyticsEvents } from '../../analytics/constants';
|
||||
import { useUsageTracker } from '../../hooks/use_usage_tracker';
|
||||
import type { LLMModel } from '../../types';
|
||||
import { useManagementLink } from '../../hooks/use_management_link';
|
||||
|
||||
interface SummarizationModelProps {
|
||||
selectedModel?: LLMModel;
|
||||
|
@ -36,6 +39,7 @@ export const SummarizationModel: React.FC<SummarizationModelProps> = ({
|
|||
onSelect,
|
||||
}) => {
|
||||
const usageTracker = useUsageTracker();
|
||||
const managementLink = useManagementLink(selectedModel?.connectorId || '');
|
||||
const onChange = (modelValue: string) => {
|
||||
const newSelectedModel = models.find((model) => getOptionValue(model) === modelValue);
|
||||
|
||||
|
@ -102,23 +106,43 @@ export const SummarizationModel: React.FC<SummarizationModelProps> = ({
|
|||
return (
|
||||
<EuiFormRow
|
||||
css={{ '.euiFormLabel': { display: 'flex', alignItems: 'center' } }}
|
||||
data-test-subj="aiConnectorTitle"
|
||||
label={
|
||||
<>
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.sidebar.summarizationModel.label"
|
||||
defaultMessage="Model"
|
||||
defaultMessage="AI Connector"
|
||||
/>{' '}
|
||||
</>
|
||||
}
|
||||
fullWidth
|
||||
>
|
||||
<EuiSuperSelect
|
||||
data-test-subj="summarizationModelSelect"
|
||||
options={modelsOption}
|
||||
valueOfSelected={selectedModel && getOptionValue(selectedModel)}
|
||||
onChange={onChange}
|
||||
fullWidth
|
||||
/>
|
||||
<EuiFlexGroup direction="row" gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<EuiSuperSelect
|
||||
data-test-subj="summarizationModelSelect"
|
||||
options={modelsOption}
|
||||
valueOfSelected={selectedModel && getOptionValue(selectedModel)}
|
||||
onChange={onChange}
|
||||
fullWidth
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
target="_blank"
|
||||
href={managementLink}
|
||||
data-test-subj="manageConnectorsLink"
|
||||
iconType="wrench"
|
||||
size="s"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.searchPlayground.sidebar.summarizationModel.manageConnectorLink',
|
||||
{
|
||||
defaultMessage: 'Manage connector',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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, { FC, PropsWithChildren } from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { UploadFileButton } from './upload_file_button';
|
||||
import { useKibana } from '../hooks/use_kibana';
|
||||
import { useSourceIndicesFields } from '../hooks/use_source_indices_field';
|
||||
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
|
||||
|
||||
// Mock hooks
|
||||
jest.mock('../hooks/use_kibana', () => ({
|
||||
useKibana: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../hooks/use_source_indices_field', () => ({
|
||||
useSourceIndicesFields: jest.fn(),
|
||||
}));
|
||||
|
||||
// Wrapper for rendering with IntlProvider
|
||||
const Wrapper: FC<PropsWithChildren<unknown>> = ({ children }) => {
|
||||
return (
|
||||
<>
|
||||
<IntlProvider locale="en">{children}</IntlProvider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
describe('UploadFileButton', () => {
|
||||
const mockUiActions = {
|
||||
getTrigger: jest.fn().mockReturnValue({
|
||||
exec: jest.fn(),
|
||||
}),
|
||||
};
|
||||
|
||||
const mockSetSelectedIndices = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
(useKibana as jest.Mock).mockReturnValue({
|
||||
services: {
|
||||
uiActions: mockUiActions,
|
||||
},
|
||||
});
|
||||
|
||||
(useSourceIndicesFields as jest.Mock).mockReturnValue({
|
||||
setIndices: mockSetSelectedIndices,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders EuiButtonEmpty when isSetup is true', () => {
|
||||
render(<UploadFileButton isSetup={true} />, { wrapper: Wrapper });
|
||||
|
||||
// Check if EuiButtonEmpty is rendered
|
||||
const button = screen.getByTestId('uploadFileButtonEmpty');
|
||||
expect(button).toBeInTheDocument();
|
||||
expect(button).toHaveTextContent('Upload a file');
|
||||
});
|
||||
|
||||
it('calls showFileUploadFlyout when EuiButtonEmpty is clicked', () => {
|
||||
render(<UploadFileButton isSetup={true} />, { wrapper: Wrapper });
|
||||
|
||||
// Click the button
|
||||
const button = screen.getByTestId('uploadFileButtonEmpty');
|
||||
fireEvent.click(button);
|
||||
|
||||
// Check if the flyout trigger was executed
|
||||
expect(mockUiActions.getTrigger).toHaveBeenCalledWith('OPEN_FILE_UPLOAD_LITE_TRIGGER');
|
||||
expect(mockUiActions.getTrigger().exec).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders EuiButton when isSetup is false', () => {
|
||||
render(<UploadFileButton isSetup={false} />, { wrapper: Wrapper });
|
||||
|
||||
// Check if EuiButton is rendered
|
||||
const button = screen.getByTestId('uploadFileButton');
|
||||
expect(button).toBeInTheDocument();
|
||||
expect(button).toHaveTextContent('Upload file');
|
||||
});
|
||||
|
||||
it('calls showFileUploadFlyout when EuiButton is clicked', () => {
|
||||
render(<UploadFileButton isSetup={false} />, { wrapper: Wrapper });
|
||||
|
||||
// Click the button
|
||||
const button = screen.getByTestId('uploadFileButton');
|
||||
fireEvent.click(button);
|
||||
|
||||
// Check if the flyout trigger was executed
|
||||
expect(mockUiActions.getTrigger).toHaveBeenCalledWith('OPEN_FILE_UPLOAD_LITE_TRIGGER');
|
||||
expect(mockUiActions.getTrigger().exec).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { EuiButton, EuiButtonEmpty } from '@elastic/eui';
|
||||
import { type FileUploadResults, OPEN_FILE_UPLOAD_LITE_TRIGGER } from '@kbn/file-upload-common';
|
||||
import { useKibana } from '../hooks/use_kibana';
|
||||
import { useSourceIndicesFields } from '../hooks/use_source_indices_field';
|
||||
|
@ -35,18 +35,32 @@ export const UploadFileButton: React.FC<Props> = ({ isSetup }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<EuiButton
|
||||
size={isSetup ? 'm' : 's'}
|
||||
fill={isSetup}
|
||||
iconType="plusInCircle"
|
||||
onClick={() => showFileUploadFlyout()}
|
||||
data-test-subj="uploadFileButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.setupPage.uploadFileLabel"
|
||||
defaultMessage="Upload file"
|
||||
/>
|
||||
</EuiButton>
|
||||
{isSetup ? (
|
||||
<EuiButtonEmpty
|
||||
flush="right"
|
||||
iconType="importAction"
|
||||
onClick={() => showFileUploadFlyout()}
|
||||
data-test-subj="uploadFileButtonEmpty"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.setupPage.uploadFileButtonEmptyLabel"
|
||||
defaultMessage="Upload a file"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
) : (
|
||||
<EuiButton
|
||||
size="s"
|
||||
fill={false}
|
||||
iconType="plusInCircle"
|
||||
onClick={() => showFileUploadFlyout()}
|
||||
data-test-subj="uploadFileButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.searchPlayground.setupPage.uploadFileLabel"
|
||||
defaultMessage="Upload file"
|
||||
/>
|
||||
</EuiButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -69,7 +69,7 @@ export default function (ftrContext: FtrProviderContext) {
|
|||
await browser.refresh();
|
||||
});
|
||||
it('show success llm button', async () => {
|
||||
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectShowSuccessLLMButton();
|
||||
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectShowSuccessLLMText();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -146,11 +146,11 @@ export function SearchPlaygroundPageProvider({ getService }: FtrProviderContext)
|
|||
async expectSuccessButtonAfterCreatingConnector(createConnector: () => Promise<void>) {
|
||||
await createConnector();
|
||||
await browser.refresh();
|
||||
await testSubjects.existOrFail('successConnectLLMButton');
|
||||
await testSubjects.existOrFail('successConnectLLMText');
|
||||
},
|
||||
|
||||
async expectShowSuccessLLMButton() {
|
||||
await testSubjects.existOrFail('successConnectLLMButton');
|
||||
async expectShowSuccessLLMText() {
|
||||
await testSubjects.existOrFail('successConnectLLMText');
|
||||
},
|
||||
},
|
||||
PlaygroundChatPage: {
|
||||
|
|
|
@ -90,7 +90,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await browser.refresh();
|
||||
});
|
||||
it('show success llm button', async () => {
|
||||
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectShowSuccessLLMButton();
|
||||
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectShowSuccessLLMText();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue