[Onboarding] Hide the semantic_text banner if there exists a semantic_text field (#210676)

In this PR, we covered the following changes:

- Do not display the banner it after semantic text fields have been
added
- Update messaging to be more explicit on the automatic chunking that is
being handled in the background

<img width="1717" alt="Screenshot 2025-02-26 at 3 53 40 PM"
src="https://github.com/user-attachments/assets/f7aecf30-b7ca-4add-a543-a76f975e372a"
/>



- - - 

Closes https://github.com/elastic/search-team/issues/7874

---------

Co-authored-by: Liam Thompson <32779855+leemthompo@users.noreply.github.com>
This commit is contained in:
Saikat Sarkar 2025-02-28 04:25:30 -07:00 committed by GitHub
parent 8fe5d7f94b
commit bbc3b451f1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 97 additions and 10 deletions

View file

@ -171,6 +171,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
engines: `${ENTERPRISE_SEARCH_DOCS}engines.html`,
indexApi: `${ELASTICSEARCH_DOCS}docs-index_.html`,
inferenceApiCreate: `${ELASTICSEARCH_DOCS}put-inference-api.html`,
inferenceApisConfigureChunking: `${ELASTICSEARCH_DOCS}inference-apis.html#infer-chunking-config`,
ingestionApis: `${ELASTICSEARCH_DOCS}search-with-elasticsearch.html`,
ingestPipelines: `${ELASTICSEARCH_DOCS}ingest-pipeline-search.html`,
knnSearch: `${ELASTICSEARCH_DOCS}knn-search.html`,

View file

@ -140,6 +140,7 @@ export interface DocLinks {
readonly engines: string;
readonly indexApi: string;
readonly inferenceApiCreate: string;
readonly inferenceApisConfigureChunking: string;
readonly ingestionApis: string;
readonly ingestPipelines: string;
readonly knnSearch: string;

View file

@ -20103,7 +20103,6 @@
"xpack.idxMgmt.indexDetails.mappings.mappingsViewButtonGroupAriaLabel": "Groupe de bouton de l'affichage des mappings",
"xpack.idxMgmt.indexDetails.mappings.reloadButtonLabel": "Recharger",
"xpack.idxMgmt.indexDetails.mappings.saveMappings": "Enregistrer les mappings",
"xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.description": "{label} Ajoutez un champ à votre mapping et choisissez \"Semantic text\" pour commencer.",
"xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.descriptionForPlatinumLicense": "{label} Mettez à niveau votre licence pour ajouter les types de champ semantic_text à vos index.\"",
"xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.dismiss": "Rejeter",
"xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.semanticTextFieldAvailable": "Le type de champ semantic_text est maintenant disponible !",

View file

@ -19963,7 +19963,6 @@
"xpack.idxMgmt.indexDetails.mappings.mappingsViewButtonGroupAriaLabel": "マッピングビューボタングループ",
"xpack.idxMgmt.indexDetails.mappings.reloadButtonLabel": "再読み込み",
"xpack.idxMgmt.indexDetails.mappings.saveMappings": "マッピングを保存",
"xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.description": "{label} 開始するには、フィールドをマッピングに追加して、Semantic_textを選択してください。",
"xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.descriptionForPlatinumLicense": "{label} semantic_textフィールド型をインデックスに追加するには、ライセンスをアップグレードしてください。'",
"xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.dismiss": "閉じる",
"xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.semanticTextFieldAvailable": "semantic_textフィールド型が利用できるようになりました。",

View file

@ -19636,7 +19636,6 @@
"xpack.idxMgmt.indexDetails.mappings.mappingsViewButtonGroupAriaLabel": "映射视图按钮组",
"xpack.idxMgmt.indexDetails.mappings.reloadButtonLabel": "重新加载",
"xpack.idxMgmt.indexDetails.mappings.saveMappings": "保存映射",
"xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.description": "{label} 将字段添加到您的映射并选择'语义文本'以开始使用。",
"xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.descriptionForPlatinumLicense": "{label} 升级许可证以将 semantic_text 字段类型添加到您的索引。'",
"xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.dismiss": "关闭",
"xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.semanticTextFieldAvailable": "semantic_text 字段类型现已可用!",

View file

@ -533,6 +533,67 @@ describe('<IndexDetailsPage />', () => {
});
});
describe('Semantic Text Banner', () => {
const mockIndexMappingResponseWithoutSemanticText: any = {
...testIndexMappings.mappings,
properties: {
...testIndexMappings.mappings.properties,
name: {
type: 'text',
},
},
};
const mockIndexMappingResponseWithSemanticText: any = {
...testIndexMappings.mappings,
properties: {
...testIndexMappings.mappings.properties,
name: {
type: 'text',
},
sem_text: {
type: 'semantic_text',
inference_id: '.elser-2-elasticsearch',
},
title: {
type: 'text',
copy_to: ['sem_text'],
},
},
};
beforeEach(async () => {
await act(async () => {
testBed = await setup({
httpSetup,
dependencies: {
core: {
application: { capabilities: { ml: { canGetTrainedModels: true } } },
},
},
});
});
});
it('semantic text banner is visible if there is no semantic_text field in the mapping', async () => {
httpRequestsMockHelpers.setLoadIndexMappingResponse(testIndexName, {
mappings: mockIndexMappingResponseWithoutSemanticText,
});
testBed.component.update();
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Mappings);
expect(testBed.actions.mappings.isSemanticTextBannerVisible()).toBe(true);
});
it('semantic text banner is not visible if there exists a semantic_text field in the mapping', async () => {
httpRequestsMockHelpers.setLoadIndexMappingResponse(testIndexName, {
mappings: mockIndexMappingResponseWithSemanticText,
});
testBed.component.update();
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Mappings);
expect(testBed.actions.mappings.isSemanticTextBannerVisible()).toBe(false);
});
});
describe('Mappings tab', () => {
beforeEach(async () => {
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Mappings);

View file

@ -42,6 +42,10 @@ describe('When semantic_text is enabled', () => {
expect(find('indexDetailsMappingsSemanticTextBanner').text()).toContain(
'semantic_text field type now available!'
);
expect(find('indexDetailsMappingsSemanticTextBanner').text()).toContain(
'Documents will be automatically chunked to fit model context limits, to avoid truncation.'
);
});
it('should hide the banner if dismiss is clicked', async () => {

View file

@ -120,6 +120,8 @@ export const DetailsPageMappingsContent: FunctionComponent<{
prefix: 'pendingFieldListId',
});
const hasSemanticText = hasSemanticTextField(state.fields);
const [isAddingFields, setAddingFields] = useState<boolean>(false);
useUnsavedChangesPrompt({
@ -222,7 +224,6 @@ export const DetailsPageMappingsContent: FunctionComponent<{
const updateMappings = useCallback(
async (forceSaveMappings?: boolean) => {
const hasSemanticText = hasSemanticTextField(state.fields);
let inferenceToModelIdMap = state.inferenceToModelIdMap;
setIsUpdatingMappings(true);
try {
@ -555,7 +556,7 @@ export const DetailsPageMappingsContent: FunctionComponent<{
</EuiFilterGroup>
</EuiFlexItem>
</EuiFlexGroup>
{hasMLPermissions && (
{hasMLPermissions && !hasSemanticText && (
<EuiFlexItem grow={true}>
<SemanticTextBanner
isSemanticTextEnabled={isSemanticTextEnabled}

View file

@ -4,10 +4,18 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui';
import {
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiLink,
EuiPanel,
EuiText,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import React from 'react';
import useLocalStorage from 'react-use/lib/useLocalStorage';
import { documentationService } from '../../../../services';
interface SemanticTextBannerProps {
isSemanticTextEnabled: boolean;
@ -31,10 +39,10 @@ const defaultLicenseMessage = (
/>
);
const platinumLicenseMessage = (
const getPlatinumLicenseMessage = () => (
<FormattedMessage
id="xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.description"
defaultMessage="{label} Add a field to your mapping and choose 'Semantic text' to get started."
defaultMessage="{label} Add a field to your mapping and choose 'Semantic text' to get started. Documents will be automatically chunked to fit model context limits, to avoid truncation. {learnMore}"
values={{
label: (
<strong>
@ -44,6 +52,14 @@ const platinumLicenseMessage = (
/>
</strong>
),
learnMore: (
<EuiLink href={documentationService.getConfigureChunkingDocLink()} target="_blank">
<FormattedMessage
id="xpack.idxMgmt.indexDetails.mappings.semanticTextBanner.learnMore"
defaultMessage="Learn more"
/>
</EuiLink>
),
}}
/>
);
@ -58,10 +74,10 @@ export function SemanticTextBanner({
return isSemanticTextBannerDisplayable && isSemanticTextEnabled ? (
<>
<EuiPanel color="accentSecondary" data-test-subj="indexDetailsMappingsSemanticTextBanner">
<EuiFlexGroup>
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<EuiText size="m" color="primary">
{isPlatinumLicense ? platinumLicenseMessage : defaultLicenseMessage}
{isPlatinumLicense ? getPlatinumLicenseMessage() : defaultLicenseMessage}
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>

View file

@ -21,6 +21,7 @@ class DocumentationService {
private indexSettings: string = '';
private indexTemplates: string = '';
private indexV1: string = '';
private inferenceApisConfigureChunking: string = '';
private mapping: string = '';
private mappingAnalyzer: string = '';
private mappingCoerce: string = '';
@ -81,6 +82,7 @@ class DocumentationService {
this.indexManagement = links.management.indexManagement;
this.indexSettings = links.elasticsearch.indexSettings;
this.indexTemplates = links.elasticsearch.indexTemplates;
this.inferenceApisConfigureChunking = links.enterpriseSearch.inferenceApisConfigureChunking;
this.indexV1 = links.apis.putIndexTemplateV1;
this.mapping = links.elasticsearch.mapping;
this.mappingAnalyzer = links.elasticsearch.mappingAnalyzer;
@ -159,6 +161,10 @@ class DocumentationService {
return this.indexManagement;
}
public getConfigureChunkingDocLink() {
return this.inferenceApisConfigureChunking;
}
public getIndicesComponentTemplate() {
return this.indicesComponentTemplate;
}