From 9cf4ca1b156132a1570d529c3f1ef271b383d5cb Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 30 Sep 2024 16:01:28 +0200 Subject: [PATCH 01/30] hide pagination when total number of items is lower than min page size option --- .../analytics_list/use_table_settings.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts index 670efb1627ef7..b5dc28454efbc 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts @@ -32,7 +32,7 @@ export interface CriteriaWithPagination extends Criteria { interface UseTableSettingsReturnValue { onTableChange: EuiBasicTableProps['onChange']; - pagination: Required>; + pagination: Required> | boolean; sorting: { sort: { field: keyof T; @@ -66,15 +66,19 @@ export function useTableSettings( [pageState, updatePageState] ); - const pagination = useMemo( - () => ({ + const pagination = useMemo(() => { + if (totalItemCount <= Math.min(...PAGE_SIZE_OPTIONS)) { + // Hide pagination if total number of items is lower that the smallest per page option + return false; + } + + return { pageIndex, pageSize, totalItemCount, pageSizeOptions: PAGE_SIZE_OPTIONS, - }), - [totalItemCount, pageIndex, pageSize] - ); + }; + }, [totalItemCount, pageIndex, pageSize]); const sorting = useMemo( () => ({ From 2f31439693198e38ef6d55656921e088a1b8aea1 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 30 Sep 2024 16:02:16 +0200 Subject: [PATCH 02/30] update layout --- .../model_management/models_list.tsx | 113 +++++++----------- 1 file changed, 45 insertions(+), 68 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 3aef951f1545e..3afbcd46e5e15 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -26,6 +26,7 @@ import { EuiTitle, EuiToolTip, type EuiSearchBarProps, + EuiText, } from '@elastic/eui'; import { groupBy, isEmpty } from 'lodash'; import { i18n } from '@kbn/i18n'; @@ -583,8 +584,7 @@ export const ModelsList: FC = ({ const columns: Array> = [ { - align: 'left', - width: '32px', + width: '5%', isExpander: true, render: (item: ModelItem) => { if (!item.stats) { @@ -610,35 +610,15 @@ export const ModelsList: FC = ({ }, { name: modelIdColumnName, - width: '15%', + width: '60%', sortable: ({ model_id: modelId }: ModelItem) => modelId, truncateText: false, textOnly: false, 'data-test-subj': 'mlModelsTableColumnId', - render: ({ description, model_id: modelId }: ModelItem) => { + render: ({ description, model_id: modelId, recommended, supported, type }: ModelItem) => { const isTechPreview = description?.includes('(Tech Preview)'); - return ( - - {modelId} - {isTechPreview ? ( - - - - ) : null} - - ); - }, - }, - { - name: i18n.translate('xpack.ml.trainedModels.modelsList.modelDescriptionHeader', { - defaultMessage: 'Description', - }), - truncateText: false, - 'data-test-subj': 'mlModelsTableColumnDescription', - render: ({ description, recommended, tags, supported }: ModelItem) => { - if (!description) return null; - const descriptionText = description.replace('(Tech Preview)', ''); + const descriptionText = description?.replace('(Tech Preview)', ''); const tooltipContent = supported === false ? ( @@ -653,46 +633,54 @@ export const ModelsList: FC = ({ /> ) : null; - return tooltipContent ? ( - - <> - {descriptionText}  - - - - ) : ( - descriptionText + return ( + + + + {modelId} + + {isTechPreview ? ( + + + + ) : null} + + + {descriptionText ? ( + + {tooltipContent ? ( + + <> + {descriptionText}  + + + + ) : ( + descriptionText + )} + + ) : null} + + {Array.isArray(type) && type.length > 0 ? ( + + {type.map((t) => ( + + + {t} + + + ))} + + ) : null} + ); }, }, { width: '15%', - field: ModelsTableToConfigMapping.type, - name: i18n.translate('xpack.ml.trainedModels.modelsList.typeHeader', { - defaultMessage: 'Type', - }), - sortable: true, - truncateText: true, - align: 'left', - render: (types: string[]) => ( - - {types.map((type) => ( - - - {type} - - - ))} - - ), - 'data-test-subj': 'mlModelsTableColumnType', - }, - { - width: '10%', name: i18n.translate('xpack.ml.trainedModels.modelsList.stateHeader', { defaultMessage: 'State', }), - align: 'left', truncateText: false, render: ({ state, downloadState }: ModelItem) => { const config = getModelStateColor(state); @@ -733,17 +721,6 @@ export const ModelsList: FC = ({ }, 'data-test-subj': 'mlModelsTableColumnDeploymentState', }, - { - width: '20%', - field: ModelsTableToConfigMapping.createdAt, - name: i18n.translate('xpack.ml.trainedModels.modelsList.createdAtHeader', { - defaultMessage: 'Created at', - }), - dataType: 'date', - render: (v: number) => dateFormatter(v), - sortable: true, - 'data-test-subj': 'mlModelsTableColumnCreatedAt', - }, { width: '15%', name: i18n.translate('xpack.ml.trainedModels.modelsList.actionsHeader', { From 85867840f65c1a2d7f9fab967a613eb7fa37eb6c Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 30 Sep 2024 16:14:03 +0200 Subject: [PATCH 03/30] remove "Not downloaded" --- .../application/model_management/get_model_state.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx b/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx index 8591a3b9e8dc9..1e3e3a92f0931 100644 --- a/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx +++ b/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx @@ -71,12 +71,7 @@ export const getModelStateColor = ( }), }; case MODEL_STATE.NOT_DOWNLOADED: - return { - color: '#d4dae5', - name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.notDownloadedName', { - defaultMessage: 'Not downloaded', - }), - }; + return null; default: return null; } From 7b463bcc6c375591fd0c21bf7bbf53d253db1095 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 30 Sep 2024 16:16:43 +0200 Subject: [PATCH 04/30] remove unused translation --- x-pack/plugins/translations/translations/fr-FR.json | 4 ---- x-pack/plugins/translations/translations/ja-JP.json | 4 ---- x-pack/plugins/translations/translations/zh-CN.json | 4 ---- 3 files changed, 12 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index d1d4e79320a21..50f77a9cf0ba5 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -30092,7 +30092,6 @@ "xpack.ml.trainedModels.modelsList.builtInModelLabel": "intégré", "xpack.ml.trainedModels.modelsList.builtInModelMessage": "Modèle intégré", "xpack.ml.trainedModels.modelsList.collapseRow": "Réduire", - "xpack.ml.trainedModels.modelsList.createdAtHeader": "Créé à", "xpack.ml.trainedModels.modelsList.deleteDisabledWithDeploymentsTooltip": "Le modèle a commencé à être déployé", "xpack.ml.trainedModels.modelsList.deleteDisabledWithInferenceServicesTooltip": "Le modèle est utilisé par l'API _inference", "xpack.ml.trainedModels.modelsList.deleteModal.approvePipelinesDeletionLabel": "Supprimer {pipelinesCount, plural, one {le pipeline} other {les pipelines}}", @@ -30147,11 +30146,9 @@ "xpack.ml.trainedModels.modelsList.forceStopDialog.selectDeploymentsLegend": "Sélectionner les déploiements à arrêter", "xpack.ml.trainedModels.modelsList.forceStopDialog.title": "Arrêter {deploymentCount, plural, one {le déploiement} other {les déploiements}} du modèle {modelId} ?", "xpack.ml.trainedModels.modelsList.mitLicenseLabel": "Licence : MIT", - "xpack.ml.trainedModels.modelsList.modelDescriptionHeader": "Description", "xpack.ml.trainedModels.modelsList.modelIdHeader": "ID", "xpack.ml.trainedModels.modelsList.modelState.downloadedName": "Paré au déploiement", "xpack.ml.trainedModels.modelsList.modelState.downloadingName": "Téléchargement...", - "xpack.ml.trainedModels.modelsList.modelState.notDownloadedName": "Non téléchargé", "xpack.ml.trainedModels.modelsList.modelState.startedName": "Déployé", "xpack.ml.trainedModels.modelsList.modelState.startingName": "Déploiement lancé...", "xpack.ml.trainedModels.modelsList.modelState.stoppingName": "Déploiement en phase d'arrêt...", @@ -30185,7 +30182,6 @@ "xpack.ml.trainedModels.modelsList.stopSuccess": "{numberOfDeployments, plural, one {Le déploiement} other {Les déploiements}} pour \"{modelId}\" {numberOfDeployments, plural, one {a bien été arrêté} other {ont bien été arrêtés}}.", "xpack.ml.trainedModels.modelsList.successfullyDeletedMessage": "{modelsCount, plural, one {Le modèle {modelIds}} other {# modèles}} {modelsCount, plural, one {a bien été supprimé} other {ont bien été supprimés}}.", "xpack.ml.trainedModels.modelsList.totalAmountLabel": "Total de modèles entraînés", - "xpack.ml.trainedModels.modelsList.typeHeader": "Type", "xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "Mettre à jour le déploiement {modelId}", "xpack.ml.trainedModels.modelsList.updateFailed": "Impossible de mettre à jour \"{modelId}\"", "xpack.ml.trainedModels.modelsList.updateSuccess": "Le déploiement pour \"{modelId}\" a bien été mis à jour.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 75cf864832ceb..6bb8ac762a060 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -29839,7 +29839,6 @@ "xpack.ml.trainedModels.modelsList.builtInModelLabel": "ビルトイン", "xpack.ml.trainedModels.modelsList.builtInModelMessage": "ビルトインモデル", "xpack.ml.trainedModels.modelsList.collapseRow": "縮小", - "xpack.ml.trainedModels.modelsList.createdAtHeader": "作成日時:", "xpack.ml.trainedModels.modelsList.deleteDisabledWithDeploymentsTooltip": "モデルはデプロイを開始しました", "xpack.ml.trainedModels.modelsList.deleteDisabledWithInferenceServicesTooltip": "モデルは_inference APIによって使用されます。", "xpack.ml.trainedModels.modelsList.deleteModal.approvePipelinesDeletionLabel": "{pipelinesCount, plural, other {パイプライン}}を削除", @@ -29894,11 +29893,9 @@ "xpack.ml.trainedModels.modelsList.forceStopDialog.selectDeploymentsLegend": "停止するデプロイを選択", "xpack.ml.trainedModels.modelsList.forceStopDialog.title": "モデル{modelId}の{deploymentCount, plural, other {デプロイ}}を停止しますか?", "xpack.ml.trainedModels.modelsList.mitLicenseLabel": "ライセンス:MIT", - "xpack.ml.trainedModels.modelsList.modelDescriptionHeader": "説明", "xpack.ml.trainedModels.modelsList.modelIdHeader": "ID", "xpack.ml.trainedModels.modelsList.modelState.downloadedName": "デプロイできます", "xpack.ml.trainedModels.modelsList.modelState.downloadingName": "ダウンロード中...", - "xpack.ml.trainedModels.modelsList.modelState.notDownloadedName": "未ダウンロード", "xpack.ml.trainedModels.modelsList.modelState.startedName": "デプロイ済み", "xpack.ml.trainedModels.modelsList.modelState.startingName": "デプロイを開始中...", "xpack.ml.trainedModels.modelsList.modelState.stoppingName": "デプロイを停止中...", @@ -29931,7 +29928,6 @@ "xpack.ml.trainedModels.modelsList.stopFailed": "\"{modelId}\"の停止に失敗しました", "xpack.ml.trainedModels.modelsList.stopSuccess": "\"{modelId}\"の{numberOfDeployments, plural, other {デプロイ}}が正常に停止しました。", "xpack.ml.trainedModels.modelsList.totalAmountLabel": "学習済みモデルの合計数", - "xpack.ml.trainedModels.modelsList.typeHeader": "型", "xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "{modelId}デプロイを更新", "xpack.ml.trainedModels.modelsList.updateFailed": "\"{modelId}\"を更新できませんでした", "xpack.ml.trainedModels.modelsList.updateSuccess": "\"{modelId}\"のデプロイが正常に更新されました。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index d7f138cdc84a2..e6f0923ff153b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -29879,7 +29879,6 @@ "xpack.ml.trainedModels.modelsList.builtInModelLabel": "内置", "xpack.ml.trainedModels.modelsList.builtInModelMessage": "内置模型", "xpack.ml.trainedModels.modelsList.collapseRow": "折叠", - "xpack.ml.trainedModels.modelsList.createdAtHeader": "创建于", "xpack.ml.trainedModels.modelsList.deleteDisabledWithDeploymentsTooltip": "模型已开始部署", "xpack.ml.trainedModels.modelsList.deleteDisabledWithInferenceServicesTooltip": "模型由 _inference API 使用", "xpack.ml.trainedModels.modelsList.deleteModal.approvePipelinesDeletionLabel": "删除{pipelinesCount, plural, other {管道}}", @@ -29934,11 +29933,9 @@ "xpack.ml.trainedModels.modelsList.forceStopDialog.selectDeploymentsLegend": "选择要停止的部署", "xpack.ml.trainedModels.modelsList.forceStopDialog.title": "停止{deploymentCount, plural, other {部署}}模型 {modelId}?", "xpack.ml.trainedModels.modelsList.mitLicenseLabel": "许可证:MIT", - "xpack.ml.trainedModels.modelsList.modelDescriptionHeader": "描述", "xpack.ml.trainedModels.modelsList.modelIdHeader": "ID", "xpack.ml.trainedModels.modelsList.modelState.downloadedName": "准备部署", "xpack.ml.trainedModels.modelsList.modelState.downloadingName": "正在下载......", - "xpack.ml.trainedModels.modelsList.modelState.notDownloadedName": "未下载", "xpack.ml.trainedModels.modelsList.modelState.startedName": "已部署", "xpack.ml.trainedModels.modelsList.modelState.startingName": "开始部署......", "xpack.ml.trainedModels.modelsList.modelState.stoppingName": "停止部署......", @@ -29972,7 +29969,6 @@ "xpack.ml.trainedModels.modelsList.stopSuccess": "已成功停止“{modelId}”的{numberOfDeployments, plural, other {部署}}。", "xpack.ml.trainedModels.modelsList.successfullyDeletedMessage": "{modelsCount, plural, one {模型 {modelIds}} other {# 个模型}}{modelsCount, plural, other {已}}成功删除", "xpack.ml.trainedModels.modelsList.totalAmountLabel": "已训练的模型总数", - "xpack.ml.trainedModels.modelsList.typeHeader": "类型", "xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "更新 {modelId} 部署", "xpack.ml.trainedModels.modelsList.updateFailed": "无法更新“{modelId}”", "xpack.ml.trainedModels.modelsList.updateSuccess": "已成功更新“{modelId}”的部署。", From c1588932321e77dc70fb200e1a8926bd45043edf Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 30 Sep 2024 17:00:48 +0200 Subject: [PATCH 05/30] e5 disclaimer --- .../src/constants/trained_models.ts | 9 +++++++++ .../model_management/models_list.tsx | 20 ++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts index 95337518361e9..9fd3483771a9f 100644 --- a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts +++ b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts @@ -120,6 +120,10 @@ export const ELASTIC_MODEL_DEFINITIONS: Record< license: 'MIT', licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small', type: ['pytorch', 'text_embedding'], + disclaimer: i18n.translate('xpack.ml.trainedModels.modelsList.e5v1Disclaimer', { + defaultMessage: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', + }), }, [E5_LINUX_OPTIMIZED_MODEL_ID]: { modelName: 'e5', @@ -138,6 +142,10 @@ export const ELASTIC_MODEL_DEFINITIONS: Record< license: 'MIT', licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small_linux-x86_64', type: ['pytorch', 'text_embedding'], + disclaimer: i18n.translate('xpack.ml.trainedModels.modelsList.e5v1Disclaimer', { + defaultMessage: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', + }), }, } as const); @@ -167,6 +175,7 @@ export interface ModelDefinition { /** Link to the external license/documentation page */ licenseUrl?: string; type?: readonly string[]; + disclaimer?: string; } export type ModelDefinitionResponse = ModelDefinition & { diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 3afbcd46e5e15..ea6e9d59884d3 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -107,6 +107,7 @@ export type ModelItem = TrainedModelConfigResponse & { softwareLicense?: string; licenseUrl?: string; downloadState?: ModelDownloadState; + disclaimer?: string; }; export type ModelItemFull = Required; @@ -291,13 +292,14 @@ export const ModelsList: FC = ({ ); const forDownload = await trainedModelsApiService.getTrainedModelDownloads(); const notDownloaded: ModelItem[] = forDownload - .filter(({ model_id: modelId, hidden, recommended, supported }) => { + .filter(({ model_id: modelId, hidden, recommended, supported, disclaimer }) => { if (idMap.has(modelId)) { const model = idMap.get(modelId)!; if (recommended) { model.recommended = true; } model.supported = supported; + model.disclaimer = disclaimer; } return !idMap.has(modelId) && !hidden; }) @@ -316,6 +318,7 @@ export const ModelsList: FC = ({ softwareLicense: modelDefinition.license, licenseUrl: modelDefinition.licenseUrl, supported: modelDefinition.supported, + disclaimer: modelDefinition.disclaimer, } as ModelItem; }); resultItems = [...resultItems, ...notDownloaded]; @@ -615,10 +618,21 @@ export const ModelsList: FC = ({ truncateText: false, textOnly: false, 'data-test-subj': 'mlModelsTableColumnId', - render: ({ description, model_id: modelId, recommended, supported, type }: ModelItem) => { + render: ({ + description, + model_id: modelId, + recommended, + supported, + type, + disclaimer, + }: ModelItem) => { const isTechPreview = description?.includes('(Tech Preview)'); - const descriptionText = description?.replace('(Tech Preview)', ''); + let descriptionText = description?.replace('(Tech Preview)', ''); + + if (disclaimer) { + descriptionText += '. ' + disclaimer; + } const tooltipContent = supported === false ? ( From d936f399bc4b8e1ed63edf1fd65129a3a67587f1 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 1 Oct 2024 11:41:51 +0200 Subject: [PATCH 06/30] auto table layout --- .../ml/public/application/model_management/models_list.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index ea6e9d59884d3..c95df3ba5491b 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -587,8 +587,8 @@ export const ModelsList: FC = ({ const columns: Array> = [ { - width: '5%', isExpander: true, + align: 'center', render: (item: ModelItem) => { if (!item.stats) { return null; @@ -613,7 +613,6 @@ export const ModelsList: FC = ({ }, { name: modelIdColumnName, - width: '60%', sortable: ({ model_id: modelId }: ModelItem) => modelId, truncateText: false, textOnly: false, @@ -691,7 +690,6 @@ export const ModelsList: FC = ({ }, }, { - width: '15%', name: i18n.translate('xpack.ml.trainedModels.modelsList.stateHeader', { defaultMessage: 'State', }), @@ -736,7 +734,6 @@ export const ModelsList: FC = ({ 'data-test-subj': 'mlModelsTableColumnDeploymentState', }, { - width: '15%', name: i18n.translate('xpack.ml.trainedModels.modelsList.actionsHeader', { defaultMessage: 'Actions', }), @@ -912,6 +909,7 @@ export const ModelsList: FC = ({
+ tableLayout={'auto'} responsiveBreakpoint={'xl'} allowNeutralSort={false} columns={columns} From 6ac5ac4890d41ef39aa1af0e34a532a5e0fb4c88 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 1 Oct 2024 13:37:02 +0200 Subject: [PATCH 07/30] update states --- .../model_management/get_model_state.tsx | 43 ++++++++++++++++--- .../model_management/models_list.tsx | 30 +++++++------ 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx b/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx index 1e3e3a92f0931..503a6e46ba47b 100644 --- a/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx +++ b/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx @@ -5,8 +5,16 @@ * 2.0. */ +import React from 'react'; import { DEPLOYMENT_STATE, MODEL_STATE, type ModelState } from '@kbn/ml-trained-models-utils'; -import type { EuiHealthProps } from '@elastic/eui'; +import { + EuiBadge, + EuiHealth, + EuiLoadingSpinner, + type EuiHealthProps, + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import type { ModelItem } from './models_list'; @@ -33,35 +41,56 @@ export const getModelDeploymentState = (model: ModelItem): ModelState | undefine export const getModelStateColor = ( state: ModelState | undefined -): { color: EuiHealthProps['color']; name: string } | null => { +): { color: EuiHealthProps['color']; name: string; component?: React.ReactNode } | null => { switch (state) { case MODEL_STATE.DOWNLOADED: return { - color: 'subdued', + color: 'success', name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.downloadedName', { - defaultMessage: 'Ready to deploy', + defaultMessage: 'Downloaded. Ready to deploy.', }), }; case MODEL_STATE.DOWNLOADING: return { color: 'primary', name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.downloadingName', { - defaultMessage: 'Downloading...', + defaultMessage: 'Downloading', }), }; case MODEL_STATE.STARTED: return { - color: 'success', + color: '#E6F9F7', name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.startedName', { defaultMessage: 'Deployed', }), + get component() { + return ( + + + {this.name} + + + ); + }, }; case MODEL_STATE.STARTING: return { color: 'success', name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.startingName', { - defaultMessage: 'Starting deployment...', + defaultMessage: 'Deploying', }), + get component() { + return ( + + + + + + {this.name} + + + ); + }, }; case MODEL_STATE.STOPPING: return { diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index c95df3ba5491b..8deb7d5dd93c8 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -698,35 +698,37 @@ export const ModelsList: FC = ({ const config = getModelStateColor(state); if (!config) return null; - const isDownloadInProgress = state === MODEL_STATE.DOWNLOADING && downloadState; + const isProgressbarVisible = + (state === MODEL_STATE.DOWNLOADING && downloadState) || state === MODEL_STATE.DOWNLOADED; - const label = ( - - {config.name} - - ); + const label = {config.name}; return ( - {isDownloadInProgress ? ( + {isProgressbarVisible ? ( - {((downloadState.downloaded_parts / downloadState.total_parts) * 100).toFixed( - 0 - ) + '%'} + {downloadState + ? ( + (downloadState.downloaded_parts / downloadState.total_parts) * + 100 + ).toFixed(0) + '%' + : '100%'} } - value={downloadState?.downloaded_parts} - max={downloadState?.total_parts} + value={downloadState?.downloaded_parts ?? 1} + max={downloadState?.total_parts ?? 1} size="xs" color={config.color} /> ) : ( - {label} + + {config.component ?? label} + )} ); From cbaf1a974d951d2dc566b91a7df0db2893bf151c Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 1 Oct 2024 13:37:46 +0200 Subject: [PATCH 08/30] remove unused import --- .../ml/public/application/model_management/models_list.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 8deb7d5dd93c8..670400128eb04 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -16,7 +16,6 @@ import { EuiCallOut, EuiFlexGroup, EuiFlexItem, - EuiHealth, EuiIcon, EuiInMemoryTable, EuiLink, From 76af7cf58a5fc108043811c5134ee557bdf6fbc1 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 1 Oct 2024 13:48:17 +0200 Subject: [PATCH 09/30] fix tags responsive layout --- .../public/application/model_management/models_list.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 670400128eb04..507f6930caa41 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -677,9 +677,11 @@ export const ModelsList: FC = ({ {type.map((t) => ( - - {t} - + + + {t} + + ))} From c25a6d26532e7d43b57b2b9fb200f3187564095c Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 1 Oct 2024 18:06:35 +0200 Subject: [PATCH 10/30] change type based on layout --- .../application/model_management/model_actions.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx index b9e0c39578349..b51be0d5bb4a5 100644 --- a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx +++ b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx @@ -8,7 +8,7 @@ import type { Action } from '@elastic/eui/src/components/basic_table/action_types'; import { i18n } from '@kbn/i18n'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import { EuiToolTip } from '@elastic/eui'; +import { EuiToolTip, useIsWithinMaxBreakpoint } from '@elastic/eui'; import React, { useCallback, useMemo, useEffect, useState } from 'react'; import { BUILT_IN_MODEL_TAG, @@ -53,6 +53,8 @@ export function useModelActions({ fetchModels: () => Promise; modelAndDeploymentIds: string[]; }): Array> { + const isMobileLayout = useIsWithinMaxBreakpoint('l'); + const { services: { application: { navigateToUrl }, @@ -132,7 +134,7 @@ export function useModelActions({ [] ); - return useMemo( + return useMemo>>( () => [ { name: i18n.translate('xpack.ml.trainedModels.modelsList.viewTrainingDataNameActionLabel', { @@ -423,7 +425,9 @@ export function useModelActions({ }), 'data-test-subj': 'mlModelsTableRowDownloadModelAction', icon: 'download', - type: 'icon', + color: 'text', + // @ts-ignore + type: isMobileLayout ? 'icon' : 'button', isPrimary: true, available: (item) => canCreateTrainedModels && item.state === MODEL_STATE.NOT_DOWNLOADED, enabled: (item) => !isLoading, @@ -540,7 +544,7 @@ export function useModelActions({ 'data-test-subj': 'mlModelsTableRowTestAction', icon: 'inputOutput', type: 'icon', - isPrimary: true, + isPrimary: false, available: isTestable, onClick: (item) => { if (isDfaTrainedModel(item) && !isBuiltInModel(item)) { @@ -612,6 +616,7 @@ export function useModelActions({ trainedModelsApiService, urlLocator, onModelDownloadRequest, + isMobileLayout, ] ); } From d838115344231e450c9a3e76b37c1e840445fa59 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Fri, 4 Oct 2024 17:10:36 +0200 Subject: [PATCH 11/30] change deploy action visibility --- .../application/model_management/model_actions.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx index b51be0d5bb4a5..2aacaae2bcdc7 100644 --- a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx +++ b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx @@ -208,9 +208,10 @@ export function useModelActions({ type: 'icon', isPrimary: true, enabled: (item) => { - return canStartStopTrainedModels && !isLoading && item.state !== MODEL_STATE.DOWNLOADING; + return canStartStopTrainedModels && !isLoading; }, - available: (item) => item.model_type === TRAINED_MODEL_TYPE.PYTORCH, + available: (item) => + item.model_type === TRAINED_MODEL_TYPE.PYTORCH && item.state === MODEL_STATE.DOWNLOADED, onClick: async (item) => { const modelDeploymentParams = await getUserInputModelDeploymentParams( item, @@ -344,6 +345,7 @@ export function useModelActions({ available: (item) => item.model_type === TRAINED_MODEL_TYPE.PYTORCH && canStartStopTrainedModels && + // Deployment can be either started, starting, or exist in a failed state (item.state === MODEL_STATE.STARTED || item.state === MODEL_STATE.STARTING) && // Only show the action if there is at least one deployment that is not used by the inference service (!Array.isArray(item.inference_apis) || @@ -545,7 +547,7 @@ export function useModelActions({ icon: 'inputOutput', type: 'icon', isPrimary: false, - available: isTestable, + available: (item) => isTestable(item, true), onClick: (item) => { if (isDfaTrainedModel(item) && !isBuiltInModel(item)) { onDfaTestAction(item); @@ -554,7 +556,7 @@ export function useModelActions({ } }, enabled: (item) => { - return canTestTrainedModels && isTestable(item, true) && !isLoading; + return canTestTrainedModels && !isLoading; }, }, { From fe46884c22f8d3270c89cd00701b7ae76aadb9a8 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 12:53:45 +0200 Subject: [PATCH 12/30] update model actions --- .../model_management/model_actions.tsx | 65 ++++++++++++------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx index 2aacaae2bcdc7..12b1f47f4e87d 100644 --- a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx +++ b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx @@ -205,13 +205,18 @@ export function useModelActions({ ), 'data-test-subj': 'mlModelsTableRowStartDeploymentAction', icon: 'play', - type: 'icon', + // @ts-ignore + type: isMobileLayout ? 'icon' : 'button', isPrimary: true, + color: 'success', enabled: (item) => { return canStartStopTrainedModels && !isLoading; }, - available: (item) => - item.model_type === TRAINED_MODEL_TYPE.PYTORCH && item.state === MODEL_STATE.DOWNLOADED, + available: (item) => { + return ( + item.model_type === TRAINED_MODEL_TYPE.PYTORCH && item.state === MODEL_STATE.DOWNLOADED + ); + }, onClick: async (item) => { const modelDeploymentParams = await getUserInputModelDeploymentParams( item, @@ -486,10 +491,16 @@ export function useModelActions({ }, { name: (model) => { - return ( + return model.state === MODEL_STATE.DOWNLOADING ? ( <> {i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { - defaultMessage: 'Delete model', + defaultMessage: 'Cancel', + })} + + ) : ( + <> + {i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { + defaultMessage: 'Delete', })} ); @@ -497,27 +508,35 @@ export function useModelActions({ description: (model: ModelItem) => { const hasDeployments = model.deployment_ids.length > 0; const { hasInferenceServices } = model; - return hasInferenceServices - ? i18n.translate( - 'xpack.ml.trainedModels.modelsList.deleteDisabledWithInferenceServicesTooltip', - { - defaultMessage: 'Model is used by the _inference API', - } - ) - : hasDeployments - ? i18n.translate( - 'xpack.ml.trainedModels.modelsList.deleteDisabledWithDeploymentsTooltip', - { - defaultMessage: 'Model has started deployments', - } - ) - : i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { - defaultMessage: 'Delete model', - }); + + if (model.state === MODEL_STATE.DOWNLOADING) { + return i18n.translate('xpack.ml.trainedModels.modelsList.cancelDownloadActionLabel', { + defaultMessage: 'Cancel download', + }); + } else if (hasInferenceServices) { + return i18n.translate( + 'xpack.ml.trainedModels.modelsList.deleteDisabledWithInferenceServicesTooltip', + { + defaultMessage: 'Model is used by the _inference API', + } + ); + } else if (hasDeployments) { + return i18n.translate( + 'xpack.ml.trainedModels.modelsList.deleteDisabledWithDeploymentsTooltip', + { + defaultMessage: 'Model has started deployments', + } + ); + } else { + return i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { + defaultMessage: 'Delete model', + }); + } }, 'data-test-subj': 'mlModelsTableRowDeleteAction', icon: 'trash', - type: 'icon', + // @ts-ignore + type: isMobileLayout ? 'icon' : 'button', color: 'danger', isPrimary: false, onClick: (model) => { From 1bb8f5d49f44cbeb69f15d165874ec68d68ce566 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 14:19:40 +0200 Subject: [PATCH 13/30] improve state update --- .../model_management/models_list.tsx | 186 ++++++++---------- 1 file changed, 79 insertions(+), 107 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 507f6930caa41..0eb2e03f9c1ee 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -5,9 +5,6 @@ * 2.0. */ -import type { FC } from 'react'; -import { useRef } from 'react'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; import type { SearchFilterConfig } from '@elastic/eui'; import { EuiBadge, @@ -22,46 +19,37 @@ import { EuiProgress, EuiSpacer, EuiSwitch, + EuiText, EuiTitle, EuiToolTip, type EuiSearchBarProps, - EuiText, } from '@elastic/eui'; -import { groupBy, isEmpty } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import type { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/basic_table'; import type { EuiTableSelectionType } from '@elastic/eui/src/components/basic_table/table_types'; -import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; -import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import { usePageUrlState } from '@kbn/ml-url-state'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { useTimefilter } from '@kbn/ml-date-picker'; -import type { DeploymentState } from '@kbn/ml-trained-models-utils'; +import { isDefined } from '@kbn/ml-is-defined'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { useStorage } from '@kbn/ml-local-storage'; import { BUILT_IN_MODEL_TAG, BUILT_IN_MODEL_TYPE, - DEPLOYMENT_STATE, - ELASTIC_MODEL_DEFINITIONS, ELASTIC_MODEL_TAG, ELASTIC_MODEL_TYPE, ELSER_ID_V1, MODEL_STATE, type ModelState, } from '@kbn/ml-trained-models-utils'; -import { isDefined } from '@kbn/ml-is-defined'; -import { useStorage } from '@kbn/ml-local-storage'; +import type { ListingPageUrlState } from '@kbn/ml-url-state'; +import { usePageUrlState } from '@kbn/ml-url-state'; import { dynamic } from '@kbn/shared-ux-utility'; +import { cloneDeep, groupBy, isEmpty, memoize } from 'lodash'; +import type { FC } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import useMountedState from 'react-use/lib/useMountedState'; -import type { ListingPageUrlState } from '@kbn/ml-url-state'; -import { getModelStateColor, getModelDeploymentState } from './get_model_state'; +import { ML_PAGES } from '../../../common/constants/locator'; import { ML_ELSER_CALLOUT_DISMISSED } from '../../../common/types/storage'; -import { TechnicalPreviewBadge } from '../components/technical_preview_badge'; -import { useModelActions } from './model_actions'; -import { ModelsTableToConfigMapping } from './config_mapping'; -import type { ModelsBarStats } from '../components/stats_bar'; -import { StatsBar } from '../components/stats_bar'; -import { useMlKibana } from '../contexts/kibana'; -import { useTrainedModelsApiService } from '../services/ml_api_service/trained_models'; import type { ModelDownloadState, ModelPipelines, @@ -69,17 +57,23 @@ import type { TrainedModelDeploymentStatsResponse, TrainedModelStat, } from '../../../common/types/trained_models'; -import { DeleteModelsModal } from './delete_models_modal'; -import { ML_PAGES } from '../../../common/constants/locator'; +import { AddInferencePipelineFlyout } from '../components/ml_inference'; +import { SavedObjectsWarning } from '../components/saved_objects_warning'; +import type { ModelsBarStats } from '../components/stats_bar'; +import { StatsBar } from '../components/stats_bar'; +import { TechnicalPreviewBadge } from '../components/technical_preview_badge'; +import { useMlKibana } from '../contexts/kibana'; +import { useEnabledFeatures } from '../contexts/ml'; import { useTableSettings } from '../data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings'; -import { useToastNotificationService } from '../services/toast_notification_service'; -import { useFieldFormatter } from '../contexts/kibana/use_field_formatter'; import { useRefresh } from '../routing/use_refresh'; -import { SavedObjectsWarning } from '../components/saved_objects_warning'; -import { TestModelAndPipelineCreationFlyout } from './test_models'; +import { useTrainedModelsApiService } from '../services/ml_api_service/trained_models'; +import { useToastNotificationService } from '../services/toast_notification_service'; +import { ModelsTableToConfigMapping } from './config_mapping'; +import { DeleteModelsModal } from './delete_models_modal'; +import { getModelDeploymentState, getModelStateColor } from './get_model_state'; +import { useModelActions } from './model_actions'; import { TestDfaModelsFlyout } from './test_dfa_models_flyout'; -import { AddInferencePipelineFlyout } from '../components/ml_inference'; -import { useEnabledFeatures } from '../contexts/ml'; +import { TestModelAndPipelineCreationFlyout } from './test_models'; type Stats = Omit; @@ -166,8 +160,6 @@ export const ModelsList: FC = ({ useTimefilter({ timeRangeSelector: false, autoRefreshSelector: true }); - const dateFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DATE); - // allow for an internally controlled page state which stores the state in the URL // or an external page state, which is passed in as a prop. // external page state is used on the management page. @@ -189,7 +181,7 @@ export const ModelsList: FC = ({ const trainedModelsApiService = useTrainedModelsApiService(); - const { displayErrorToast, displaySuccessToast } = useToastNotificationService(); + const { displayErrorToast } = useToastNotificationService(); const [isInitialized, setIsInitialized] = useState(false); const [isLoading, setIsLoading] = useState(false); @@ -220,28 +212,9 @@ export const ModelsList: FC = ({ }, [items]); /** - * Checks if the model download complete. + * Fetch of model definitions available for download needs to happen only once */ - const isDownloadComplete = useCallback( - async (modelId: string): Promise => { - try { - const response = await trainedModelsApiService.getTrainedModels(modelId, { - include: 'definition_status', - }); - // @ts-ignore - return !!response[0]?.fully_defined; - } catch (error) { - displayErrorToast( - error, - i18n.translate('xpack.ml.trainedModels.modelsList.downloadStatusCheckErrorMessage', { - defaultMessage: 'Failed to check download status', - }) - ); - } - return false; - }, - [trainedModelsApiService, displayErrorToast] - ); + const getTrainedModelDownloads = memoize(trainedModelsApiService.getTrainedModelDownloads); /** * Fetches trained models. @@ -289,7 +262,11 @@ export const ModelsList: FC = ({ const idMap = new Map( resultItems.map((model) => [model.model_id, model]) ); - const forDownload = await trainedModelsApiService.getTrainedModelDownloads(); + /** + * Fetches model definitions available for download + */ + const forDownload = await getTrainedModelDownloads(); + const notDownloaded: ModelItem[] = forDownload .filter(({ model_id: modelId, hidden, recommended, supported, disclaimer }) => { if (idMap.has(modelId)) { @@ -323,18 +300,17 @@ export const ModelsList: FC = ({ resultItems = [...resultItems, ...notDownloaded]; } - setItems(resultItems); - - if (expandedItemsToRefresh.length > 0) { - await fetchModelsStats(expandedItemsToRefresh); - - setItemIdToExpandedRowMap( - expandedItemsToRefresh.reduce((acc, item) => { - acc[item.model_id] = ; - return acc; - }, {} as Record) - ); - } + setItems((prevItems) => { + // Need to merge existing items with new items + // to preserve state and download status + return resultItems.map((item) => { + return { + ...item, + state: item.state ?? prevItems.find((i) => i.model_id === item.model_id)?.state, + downloadState: prevItems.find((i) => i.model_id === item.model_id)?.downloadState, + }; + }); + }); } catch (error) { displayErrorToast( error, @@ -343,7 +319,9 @@ export const ModelsList: FC = ({ }) ); } + setIsInitialized(true); + setIsLoading(false); await fetchDownloadStatus(); @@ -402,20 +380,6 @@ export const ModelsList: FC = ({ return c.reason ?? ''; }, ''); }); - - const elasticModels = models.filter((model) => - Object.hasOwn(ELASTIC_MODEL_DEFINITIONS, model.model_id) - ); - if (elasticModels.length > 0) { - for (const model of elasticModels) { - if (Object.values(DEPLOYMENT_STATE).includes(model.state as DeploymentState)) { - // no need to check for the download status if the model has been deployed - continue; - } - const isDownloaded = await isDownloadComplete(model.model_id); - model.state = isDownloaded ? MODEL_STATE.DOWNLOADED : MODEL_STATE.DOWNLOADING; - } - } } return true; @@ -432,6 +396,8 @@ export const ModelsList: FC = ({ }, []); const downLoadStatusFetchInProgress = useRef(false); + const abortedDownload = useRef(new Set()); + /** * Updates model list with download status */ @@ -451,37 +417,37 @@ export const ModelsList: FC = ({ if (isMounted()) { setItems((prevItems) => { return prevItems.map((item) => { - const newItem = { ...item }; + if (item.model_type !== 'pytorch') { + return item; + } + const newItem = cloneDeep(item); if (downloadStatus[item.model_id]) { + newItem.state = MODEL_STATE.DOWNLOADING; newItem.downloadState = downloadStatus[item.model_id]; } else { - if (downloadInProgress.has(item.model_id)) { + /* Unfortunately, model download status does not report 100% download state, only from 1 to 99. Hence, there might be 3 cases + * 1. Model is not downloaded at all + * 2. Model download was in progress and finished + * 3. Model download was in progress and aborted + */ + delete newItem.downloadState; + + if (abortedDownload.current.has(item.model_id)) { + // Change downloading state to not downloaded + newItem.state = MODEL_STATE.NOT_DOWNLOADED; + abortedDownload.current.delete(item.model_id); + } else if (downloadInProgress.has(item.model_id) || !newItem.state) { // Change downloading state to downloaded - delete newItem.downloadState; newItem.state = MODEL_STATE.DOWNLOADED; } + + downloadInProgress.delete(item.model_id); } return newItem; }); }); - } - - const downloadedModelIds = Array.from(downloadInProgress).filter( - (v) => !downloadStatus[v] - ); - if (downloadedModelIds.length > 0) { - // Show success toast - displaySuccessToast( - i18n.translate('xpack.ml.trainedModels.modelsList.downloadCompleteSuccess', { - defaultMessage: - '"{modelIds}" {modelIdsLength, plural, one {has} other {have}} been downloaded successfully.', - values: { - modelIds: downloadedModelIds.join(', '), - modelIdsLength: downloadedModelIds.length, - }, - }) - ); + // setIsLoading(false); } Object.keys(downloadStatus).forEach((modelId) => { @@ -489,9 +455,6 @@ export const ModelsList: FC = ({ downloadInProgress.add(modelId); } }); - downloadedModelIds.forEach((v) => { - downloadInProgress.delete(v); - }); if (isEmpty(downloadStatus)) { downLoadStatusFetchInProgress.current = false; @@ -504,7 +467,7 @@ export const ModelsList: FC = ({ downLoadStatusFetchInProgress.current = false; } }, - [trainedModelsApiService, displaySuccessToast, isMounted] + [trainedModelsApiService, isMounted] ); /** @@ -695,6 +658,7 @@ export const ModelsList: FC = ({ defaultMessage: 'State', }), truncateText: false, + width: '200px', render: ({ state, downloadState }: ModelItem) => { const config = getModelStateColor(state); if (!config) return null; @@ -740,6 +704,7 @@ export const ModelsList: FC = ({ name: i18n.translate('xpack.ml.trainedModels.modelsList.actionsHeader', { defaultMessage: 'Actions', }), + width: '200px', actions, 'data-test-subj': 'mlModelsTableColumnActions', }, @@ -966,7 +931,14 @@ export const ModelsList: FC = ({ {modelsToDelete.length > 0 && ( { + modelsToDelete.forEach((model) => { + if (model.state === MODEL_STATE.DOWNLOADING) { + abortedDownload.current.add(model.model_id); + } + }); + setModelsToDelete([]); + if (refreshList) { fetchModelsData(); } From ee36bada88b2c47312b552cc1ba421e75236e9f0 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 14:54:53 +0200 Subject: [PATCH 14/30] working state update --- .../application/model_management/models_list.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 0eb2e03f9c1ee..69839bd1276cb 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -304,10 +304,15 @@ export const ModelsList: FC = ({ // Need to merge existing items with new items // to preserve state and download status return resultItems.map((item) => { + const prevItem = prevItems.find((i) => i.model_id === item.model_id); return { ...item, - state: item.state ?? prevItems.find((i) => i.model_id === item.model_id)?.state, - downloadState: prevItems.find((i) => i.model_id === item.model_id)?.downloadState, + ...(prevItem?.state === MODEL_STATE.DOWNLOADING + ? { + state: prevItem.state, + downloadState: prevItem.downloadState, + } + : {}), }; }); }); @@ -403,9 +408,10 @@ export const ModelsList: FC = ({ */ const fetchDownloadStatus = useCallback( /** + * @param initRequest If true, resolves with the first download status * @param downloadInProgress Set of model ids that reports download in progress */ - async (downloadInProgress: Set = new Set()) => { + async (initRequest: boolean = true, downloadInProgress: Set = new Set()) => { // Allows only single fetch to be in progress if (downLoadStatusFetchInProgress.current && downloadInProgress.size === 0) return; @@ -421,6 +427,7 @@ export const ModelsList: FC = ({ return item; } const newItem = cloneDeep(item); + if (downloadStatus[item.model_id]) { newItem.state = MODEL_STATE.DOWNLOADING; newItem.downloadState = downloadStatus[item.model_id]; @@ -462,7 +469,7 @@ export const ModelsList: FC = ({ } await new Promise((resolve) => setTimeout(resolve, DOWNLOAD_POLL_INTERVAL)); - await fetchDownloadStatus(downloadInProgress); + await fetchDownloadStatus(false, downloadInProgress); } catch (e) { downLoadStatusFetchInProgress.current = false; } From 869027699eac01edbc66007bc0803ce65d5eee75 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 15:09:30 +0200 Subject: [PATCH 15/30] cleanup --- .../ml/public/application/model_management/models_list.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 69839bd1276cb..b505825cb68b9 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -408,10 +408,9 @@ export const ModelsList: FC = ({ */ const fetchDownloadStatus = useCallback( /** - * @param initRequest If true, resolves with the first download status * @param downloadInProgress Set of model ids that reports download in progress */ - async (initRequest: boolean = true, downloadInProgress: Set = new Set()) => { + async (downloadInProgress: Set = new Set()) => { // Allows only single fetch to be in progress if (downLoadStatusFetchInProgress.current && downloadInProgress.size === 0) return; @@ -469,7 +468,7 @@ export const ModelsList: FC = ({ } await new Promise((resolve) => setTimeout(resolve, DOWNLOAD_POLL_INTERVAL)); - await fetchDownloadStatus(false, downloadInProgress); + await fetchDownloadStatus(downloadInProgress); } catch (e) { downLoadStatusFetchInProgress.current = false; } From 7c8f2712f56477947cc0cdfd753b852ad74543d2 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 15:11:46 +0200 Subject: [PATCH 16/30] remove success toasts --- .../model_management/model_actions.tsx | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx index 12b1f47f4e87d..2228ac8b66292 100644 --- a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx +++ b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx @@ -242,14 +242,6 @@ export function useModelActions({ : {}), } ); - displaySuccessToast( - i18n.translate('xpack.ml.trainedModels.modelsList.startSuccess', { - defaultMessage: 'Deployment for "{modelId}" has been started successfully.', - values: { - modelId: item.model_id, - }, - }) - ); await fetchModels(); } catch (e) { displayErrorToast( @@ -382,16 +374,6 @@ export function useModelActions({ force: requireForceStop, } ); - displaySuccessToast( - i18n.translate('xpack.ml.trainedModels.modelsList.stopSuccess', { - defaultMessage: - '{numberOfDeployments, plural, one {Deployment} other {Deployments}} for "{modelId}" has been stopped successfully.', - values: { - modelId: item.model_id, - numberOfDeployments: deploymentIds.length, - }, - }) - ); if (Object.values(results).some((r) => r.error !== undefined)) { Object.entries(results).forEach(([id, r]) => { if (r.error !== undefined) { From 9d6d7d59d1b15cec7a0ba208c5bdbe8b148448d7 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 15:17:49 +0200 Subject: [PATCH 17/30] remove unused translation strings --- x-pack/plugins/translations/translations/fr-FR.json | 4 ---- x-pack/plugins/translations/translations/ja-JP.json | 4 ---- x-pack/plugins/translations/translations/zh-CN.json | 4 ---- 3 files changed, 12 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 50f77a9cf0ba5..2512f007d53a7 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -30106,9 +30106,7 @@ "xpack.ml.trainedModels.modelsList.deleteModelsButtonLabel": "Supprimer", "xpack.ml.trainedModels.modelsList.deployModelActionLabel": "Déployer le modèle", "xpack.ml.trainedModels.modelsList.disableSelectableMessage": "Le modèle a des pipelines associés", - "xpack.ml.trainedModels.modelsList.downloadCompleteSuccess": "\"{modelIds}\" {modelIdsLength, plural, one {a été téléchargé} other {ont été téléchargés}} avec succès.", "xpack.ml.trainedModels.modelsList.downloadFailed": "Échec du téléchargement de \"{modelId}\"", - "xpack.ml.trainedModels.modelsList.downloadStatusCheckErrorMessage": "Échec de la vérification du statut du téléchargement", "xpack.ml.trainedModels.modelsList.e5Title": "E5 (EmbEddings from bidirEctional Encoder rEpresentations)", "xpack.ml.trainedModels.modelsList.e5v1Description": "E5 (EmbEddings from bidirEctional Encoder rEpresentations)", "xpack.ml.trainedModels.modelsList.e5v1x86Description": "E5 (EmbEddings from bidirEctional Encoder rEpresentations), optimisé for linux-x86_64", @@ -30175,11 +30173,9 @@ "xpack.ml.trainedModels.modelsList.startDeployment.updateButton": "Mettre à jour", "xpack.ml.trainedModels.modelsList.startDeployment.viewElserDocLink": "Afficher la documentation", "xpack.ml.trainedModels.modelsList.startFailed": "Impossible de démarrer \"{modelId}\"", - "xpack.ml.trainedModels.modelsList.startSuccess": "Le déploiement pour \"{modelId}\" a bien été démarré.", "xpack.ml.trainedModels.modelsList.stateHeader": "État", "xpack.ml.trainedModels.modelsList.stopDeploymentWarning": "Impossible d'arrêter \"{deploymentId}\"", "xpack.ml.trainedModels.modelsList.stopFailed": "Impossible d'arrêter \"{modelId}\"", - "xpack.ml.trainedModels.modelsList.stopSuccess": "{numberOfDeployments, plural, one {Le déploiement} other {Les déploiements}} pour \"{modelId}\" {numberOfDeployments, plural, one {a bien été arrêté} other {ont bien été arrêtés}}.", "xpack.ml.trainedModels.modelsList.successfullyDeletedMessage": "{modelsCount, plural, one {Le modèle {modelIds}} other {# modèles}} {modelsCount, plural, one {a bien été supprimé} other {ont bien été supprimés}}.", "xpack.ml.trainedModels.modelsList.totalAmountLabel": "Total de modèles entraînés", "xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "Mettre à jour le déploiement {modelId}", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 6bb8ac762a060..a7c0ff2693d73 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -29853,9 +29853,7 @@ "xpack.ml.trainedModels.modelsList.deleteModelsButtonLabel": "削除", "xpack.ml.trainedModels.modelsList.deployModelActionLabel": "モデルをデプロイ", "xpack.ml.trainedModels.modelsList.disableSelectableMessage": "モデルにはパイプラインが関連付けられています", - "xpack.ml.trainedModels.modelsList.downloadCompleteSuccess": "\"{modelIds}\" {modelIdsLength, plural, other {が}}正常にダウンロードされました。", "xpack.ml.trainedModels.modelsList.downloadFailed": "\"{modelId}\"をダウンロードできませんでした", - "xpack.ml.trainedModels.modelsList.downloadStatusCheckErrorMessage": "ダウンロードステータスを確認できませんでした", "xpack.ml.trainedModels.modelsList.e5Title": "E5(bidirEctional Encoder rEpresentationsからのEmbEddings)", "xpack.ml.trainedModels.modelsList.e5v1Description": "E5(bidirEctional Encoder rEpresentationsからのEmbEddings)", "xpack.ml.trainedModels.modelsList.e5v1x86Description": "E5(bidirEctional Encoder rEpresentationsからのEmbEddings)、inux-x86_64向けに最適化", @@ -29922,11 +29920,9 @@ "xpack.ml.trainedModels.modelsList.startDeployment.updateButton": "更新", "xpack.ml.trainedModels.modelsList.startDeployment.viewElserDocLink": "ドキュメンテーションを表示", "xpack.ml.trainedModels.modelsList.startFailed": "\"{modelId}\"の開始に失敗しました", - "xpack.ml.trainedModels.modelsList.startSuccess": "\"{modelId}\"のデプロイが正常に開始しました。", "xpack.ml.trainedModels.modelsList.stateHeader": "ステータス", "xpack.ml.trainedModels.modelsList.stopDeploymentWarning": "\"{deploymentId}\"を停止できませんでした", "xpack.ml.trainedModels.modelsList.stopFailed": "\"{modelId}\"の停止に失敗しました", - "xpack.ml.trainedModels.modelsList.stopSuccess": "\"{modelId}\"の{numberOfDeployments, plural, other {デプロイ}}が正常に停止しました。", "xpack.ml.trainedModels.modelsList.totalAmountLabel": "学習済みモデルの合計数", "xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "{modelId}デプロイを更新", "xpack.ml.trainedModels.modelsList.updateFailed": "\"{modelId}\"を更新できませんでした", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index e6f0923ff153b..f200026238f5b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -29893,9 +29893,7 @@ "xpack.ml.trainedModels.modelsList.deleteModelsButtonLabel": "删除", "xpack.ml.trainedModels.modelsList.deployModelActionLabel": "部署模型", "xpack.ml.trainedModels.modelsList.disableSelectableMessage": "模型有关联的管道", - "xpack.ml.trainedModels.modelsList.downloadCompleteSuccess": "“{modelIds}”{modelIdsLength, plural, other {已}}成功下载。", "xpack.ml.trainedModels.modelsList.downloadFailed": "无法下载“{modelId}”", - "xpack.ml.trainedModels.modelsList.downloadStatusCheckErrorMessage": "无法检查下载状态", "xpack.ml.trainedModels.modelsList.e5Title": "E5 (EmbEddings from bidirEctional Encoder rEpresentations)", "xpack.ml.trainedModels.modelsList.e5v1Description": "E5 (EmbEddings from bidirEctional Encoder rEpresentations)", "xpack.ml.trainedModels.modelsList.e5v1x86Description": "针对 linux-x86_64 进行了优化的 E5 (EmbEddings from bidirEctional Encoder rEpresentations)", @@ -29962,11 +29960,9 @@ "xpack.ml.trainedModels.modelsList.startDeployment.updateButton": "更新", "xpack.ml.trainedModels.modelsList.startDeployment.viewElserDocLink": "查看文档", "xpack.ml.trainedModels.modelsList.startFailed": "无法启动“{modelId}”", - "xpack.ml.trainedModels.modelsList.startSuccess": "已成功启动“{modelId}”的部署。", "xpack.ml.trainedModels.modelsList.stateHeader": "状态", "xpack.ml.trainedModels.modelsList.stopDeploymentWarning": "无法停止“{deploymentId}”", "xpack.ml.trainedModels.modelsList.stopFailed": "无法停止“{modelId}”", - "xpack.ml.trainedModels.modelsList.stopSuccess": "已成功停止“{modelId}”的{numberOfDeployments, plural, other {部署}}。", "xpack.ml.trainedModels.modelsList.successfullyDeletedMessage": "{modelsCount, plural, one {模型 {modelIds}} other {# 个模型}}{modelsCount, plural, other {已}}成功删除", "xpack.ml.trainedModels.modelsList.totalAmountLabel": "已训练的模型总数", "xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "更新 {modelId} 部署", From 48a60bad3b205513ef8247af129d22020b1cc9a5 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 15:23:22 +0200 Subject: [PATCH 18/30] remove redundant stats call --- .../ml/public/application/model_management/models_list.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index b505825cb68b9..92ed507b2d332 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -547,7 +547,6 @@ export const ModelsList: FC = ({ if (itemIdToExpandedRowMapValues[item.model_id]) { delete itemIdToExpandedRowMapValues[item.model_id]; } else { - await fetchModelsStats([item]); itemIdToExpandedRowMapValues[item.model_id] = ; } setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues); From ce3ca940e062b37fa3f9027252b15b3be440a80f Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 15:59:30 +0200 Subject: [PATCH 19/30] spinner for the stopping state --- .../model_management/get_model_state.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx b/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx index 503a6e46ba47b..65b3299a9f4cc 100644 --- a/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx +++ b/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx @@ -96,8 +96,20 @@ export const getModelStateColor = ( return { color: 'accent', name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.stoppingName', { - defaultMessage: 'Stopping deployment...', + defaultMessage: 'Stopping', }), + get component() { + return ( + + + + + + {this.name} + + + ); + }, }; case MODEL_STATE.NOT_DOWNLOADED: return null; From 6b2ade8a2928afb778683bc71dda7efb0f19e4d4 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 16:08:48 +0200 Subject: [PATCH 20/30] update states based on the latest prototype --- .../model_management/get_model_state.tsx | 36 +++++++++---------- .../model_management/models_list.tsx | 9 +++-- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx b/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx index 65b3299a9f4cc..d8bf2b8084a6a 100644 --- a/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx +++ b/x-pack/plugins/ml/public/application/model_management/get_model_state.tsx @@ -14,6 +14,7 @@ import { type EuiHealthProps, EuiFlexGroup, EuiFlexItem, + EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import type { ModelItem } from './models_list'; @@ -47,7 +48,7 @@ export const getModelStateColor = ( return { color: 'success', name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.downloadedName', { - defaultMessage: 'Downloaded. Ready to deploy.', + defaultMessage: 'Ready to deploy', }), }; case MODEL_STATE.DOWNLOADING: @@ -81,14 +82,14 @@ export const getModelStateColor = ( }), get component() { return ( - - - - - - {this.name} - - + + + + + + {this.name} + + ); }, }; @@ -100,19 +101,18 @@ export const getModelStateColor = ( }), get component() { return ( - - - - - - {this.name} - - + + + + + + {this.name} + + ); }, }; case MODEL_STATE.NOT_DOWNLOADED: - return null; default: return null; } diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 92ed507b2d332..05699769fc2a4 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -668,10 +668,13 @@ export const ModelsList: FC = ({ const config = getModelStateColor(state); if (!config) return null; - const isProgressbarVisible = - (state === MODEL_STATE.DOWNLOADING && downloadState) || state === MODEL_STATE.DOWNLOADED; + const isProgressbarVisible = state === MODEL_STATE.DOWNLOADING && downloadState; - const label = {config.name}; + const label = ( + + {config.name} + + ); return ( From 74adf166de23e950ef68f6582c5fff9158515ac2 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 16:23:56 +0200 Subject: [PATCH 21/30] remove success deletion toast --- .../model_management/delete_models_modal.tsx | 12 +----------- x-pack/plugins/translations/translations/fr-FR.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/delete_models_modal.tsx b/x-pack/plugins/ml/public/application/model_management/delete_models_modal.tsx index 1e08ae9874567..0f5c515c22776 100644 --- a/x-pack/plugins/ml/public/application/model_management/delete_models_modal.tsx +++ b/x-pack/plugins/ml/public/application/model_management/delete_models_modal.tsx @@ -35,7 +35,7 @@ interface DeleteModelsModalProps { export const DeleteModelsModal: FC = ({ models, onClose }) => { const trainedModelsApiService = useTrainedModelsApiService(); - const { displayErrorToast, displaySuccessToast } = useToastNotificationService(); + const { displayErrorToast } = useToastNotificationService(); const [canDeleteModel, setCanDeleteModel] = useState(false); const [deletePipelines, setDeletePipelines] = useState(false); @@ -66,16 +66,6 @@ export const DeleteModelsModal: FC = ({ models, onClose }) ) ); - displaySuccessToast( - i18n.translate('xpack.ml.trainedModels.modelsList.successfullyDeletedMessage', { - defaultMessage: - '{modelsCount, plural, one {Model {modelIds}} other {# models}} {modelsCount, plural, one {has} other {have}} been successfully deleted', - values: { - modelsCount: modelIds.length, - modelIds: modelIds.join(', '), - }, - }) - ); } catch (error) { displayErrorToast( error, diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index c587af2128811..4ecddd0b95458 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -30157,7 +30157,6 @@ "xpack.ml.trainedModels.modelsList.stateHeader": "État", "xpack.ml.trainedModels.modelsList.stopDeploymentWarning": "Impossible d'arrêter \"{deploymentId}\"", "xpack.ml.trainedModels.modelsList.stopFailed": "Impossible d'arrêter \"{modelId}\"", - "xpack.ml.trainedModels.modelsList.successfullyDeletedMessage": "{modelsCount, plural, one {Le modèle {modelIds}} other {# modèles}} {modelsCount, plural, one {a bien été supprimé} other {ont bien été supprimés}}.", "xpack.ml.trainedModels.modelsList.totalAmountLabel": "Total de modèles entraînés", "xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "Mettre à jour le déploiement {modelId}", "xpack.ml.trainedModels.modelsList.updateFailed": "Impossible de mettre à jour \"{modelId}\"", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f17b83924a2d5..341d57872f056 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -29944,7 +29944,6 @@ "xpack.ml.trainedModels.modelsList.stateHeader": "状态", "xpack.ml.trainedModels.modelsList.stopDeploymentWarning": "无法停止“{deploymentId}”", "xpack.ml.trainedModels.modelsList.stopFailed": "无法停止“{modelId}”", - "xpack.ml.trainedModels.modelsList.successfullyDeletedMessage": "{modelsCount, plural, one {模型 {modelIds}} other {# 个模型}}{modelsCount, plural, other {已}}成功删除", "xpack.ml.trainedModels.modelsList.totalAmountLabel": "已训练的模型总数", "xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "更新 {modelId} 部署", "xpack.ml.trainedModels.modelsList.updateFailed": "无法更新“{modelId}”", From 6a0628bdd73899190e1b60eb0439b447a52997f4 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 17:14:39 +0200 Subject: [PATCH 22/30] fix check for pytorch --- .../ml/public/application/model_management/models_list.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 05699769fc2a4..1a0d4b7cea99a 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -422,7 +422,7 @@ export const ModelsList: FC = ({ if (isMounted()) { setItems((prevItems) => { return prevItems.map((item) => { - if (item.model_type !== 'pytorch') { + if (!item.type?.includes('pytorch')) { return item; } const newItem = cloneDeep(item); @@ -452,8 +452,6 @@ export const ModelsList: FC = ({ return newItem; }); }); - - // setIsLoading(false); } Object.keys(downloadStatus).forEach((modelId) => { @@ -663,7 +661,7 @@ export const ModelsList: FC = ({ defaultMessage: 'State', }), truncateText: false, - width: '200px', + width: '150px', render: ({ state, downloadState }: ModelItem) => { const config = getModelStateColor(state); if (!config) return null; From eb9f76f15da77bc242667e071fe470117ebdacd6 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 17:41:41 +0200 Subject: [PATCH 23/30] update jest tests --- .../server/models/model_management/model_provider.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts b/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts index 33530bade5fcf..0b9b93720234d 100644 --- a/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts +++ b/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts @@ -89,6 +89,8 @@ describe('modelsProvider', () => { { config: { input: { field_names: ['text_field'] } }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations)', + disclaimer: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', model_id: '.multilingual-e5-small', default: true, supported: true, @@ -103,6 +105,8 @@ describe('modelsProvider', () => { config: { input: { field_names: ['text_field'] } }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations), optimized for linux-x86_64', + disclaimer: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', model_id: '.multilingual-e5-small_linux-x86_64', os: 'Linux', recommended: true, @@ -175,6 +179,8 @@ describe('modelsProvider', () => { { config: { input: { field_names: ['text_field'] } }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations)', + disclaimer: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', model_id: '.multilingual-e5-small', recommended: true, supported: true, @@ -189,6 +195,8 @@ describe('modelsProvider', () => { config: { input: { field_names: ['text_field'] } }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations), optimized for linux-x86_64', + disclaimer: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', model_id: '.multilingual-e5-small_linux-x86_64', os: 'Linux', supported: false, From 82cb2b1a0f6eddeab2be427795f5a9f68314a748 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 17:43:05 +0200 Subject: [PATCH 24/30] update api tests --- .../api_integration/apis/ml/trained_models/model_downloads.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts b/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts index 4e229c133b4fd..4e5fd70314495 100644 --- a/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts +++ b/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts @@ -100,6 +100,8 @@ export default ({ getService }: FtrProviderContext) => { }, }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations)', + disclaimer: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', license: 'MIT', licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small', type: ['pytorch', 'text_embedding'], @@ -119,6 +121,8 @@ export default ({ getService }: FtrProviderContext) => { }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations), optimized for linux-x86_64', + disclaimer: + 'This E5 model, as defined, hosted, integrated and used in conjunction with our other Elastic Software is covered by our standard warranty.', license: 'MIT', licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small_linux-x86_64', type: ['pytorch', 'text_embedding'], From a8050da6f04db76cf012ed4f6c902f70d9742822 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 7 Oct 2024 17:55:15 +0200 Subject: [PATCH 25/30] update functional tests --- .../application/model_management/models_list.tsx | 8 ++++++-- .../functional/services/ml/trained_models_table.ts | 12 +++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 1a0d4b7cea99a..78f8f4f77e7db 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -615,7 +615,7 @@ export const ModelsList: FC = ({ - {modelId} + {modelId} {isTechPreview ? ( @@ -625,7 +625,11 @@ export const ModelsList: FC = ({ {descriptionText ? ( - + {tooltipContent ? ( <> diff --git a/x-pack/test/functional/services/ml/trained_models_table.ts b/x-pack/test/functional/services/ml/trained_models_table.ts index 990ca6c1ed37a..91e57f8c63569 100644 --- a/x-pack/test/functional/services/ml/trained_models_table.ts +++ b/x-pack/test/functional/services/ml/trained_models_table.ts @@ -52,17 +52,16 @@ export function TrainedModelsTableProvider( id: string; description: string; modelTypes: string[]; - createdAt: string; state: string; } = { id: $tr .findTestSubject('mlModelsTableColumnId') - .find('.euiTableCellContent') + .findTestSubject('mlModelsTableColumnIdValueId') .text() .trim(), description: $tr - .findTestSubject('mlModelsTableColumnDescription') - .find('.euiTableCellContent') + .findTestSubject('mlModelsTableColumnId') + .findTestSubject('mlModelsTableColumnIdValueDescription') .text() .trim(), modelTypes, @@ -71,11 +70,6 @@ export function TrainedModelsTableProvider( .find('.euiTableCellContent') .text() .trim(), - createdAt: $tr - .findTestSubject('mlModelsTableColumnCreatedAt') - .find('.euiTableCellContent') - .text() - .trim(), }; rows.push(rowObject); From aa9db4a63fb3bba6536dce02336b5b84986cdecb Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 8 Oct 2024 14:22:58 +0200 Subject: [PATCH 26/30] fix tooltip, update useTableSettings signature --- .../analytics_list/use_table_settings.ts | 37 ++++++++++++++++--- .../model_management/models_list.tsx | 13 +++---- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts index b5dc28454efbc..fa24a65c425bc 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts @@ -30,9 +30,11 @@ export interface CriteriaWithPagination extends Criteria { }; } -interface UseTableSettingsReturnValue { +interface UseTableSettingsReturnValue { onTableChange: EuiBasicTableProps['onChange']; - pagination: Required> | boolean; + pagination: HidePagination extends true + ? Required> | boolean + : Required>; sorting: { sort: { field: keyof T; @@ -44,8 +46,31 @@ interface UseTableSettingsReturnValue { export function useTableSettings( totalItemCount: number, pageState: ListingPageUrlState, - updatePageState: (update: Partial) => void -): UseTableSettingsReturnValue { + updatePageState: (update: Partial) => void, + hide: true +): UseTableSettingsReturnValue; + +export function useTableSettings( + totalItemCount: number, + pageState: ListingPageUrlState, + updatePageState: (update: Partial) => void, + hide?: false +): UseTableSettingsReturnValue; + +/** + * + * @param totalItemCount + * @param pageState + * @param updatePageState + * @param hide If true, hides pagination when total number of items is lower that the smallest per page option + * @returns + */ +export function useTableSettings( + totalItemCount: number, + pageState: ListingPageUrlState, + updatePageState: (update: Partial) => void, + hide: boolean = false +): UseTableSettingsReturnValue { const { pageIndex, pageSize, sortField, sortDirection } = pageState; const onTableChange: EuiBasicTableProps['onChange'] = useCallback( @@ -67,7 +92,7 @@ export function useTableSettings( ); const pagination = useMemo(() => { - if (totalItemCount <= Math.min(...PAGE_SIZE_OPTIONS)) { + if (hide && totalItemCount <= Math.min(...PAGE_SIZE_OPTIONS)) { // Hide pagination if total number of items is lower that the smallest per page option return false; } @@ -78,7 +103,7 @@ export function useTableSettings( totalItemCount, pageSizeOptions: PAGE_SIZE_OPTIONS, }; - }, [totalItemCount, pageIndex, pageSize]); + }, [totalItemCount, pageIndex, pageSize, hide]); const sorting = useMemo( () => ({ diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 78f8f4f77e7db..a0ebef1cf981b 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -630,16 +630,12 @@ export const ModelsList: FC = ({ size={'xs'} data-test-subj="mlModelsTableColumnIdValueDescription" > + {descriptionText}  {tooltipContent ? ( - <> - {descriptionText}  - - + - ) : ( - descriptionText - )} + ) : null} ) : null} @@ -802,7 +798,8 @@ export const ModelsList: FC = ({ const { onTableChange, pagination, sorting } = useTableSettings( items.length, pageState, - updatePageState + updatePageState, + true ); const search: EuiSearchBarProps = { From 34c651631e4d861c5b8c2977b6c866ea728889a2 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 8 Oct 2024 14:25:24 +0200 Subject: [PATCH 27/30] remove createdAt assertion --- x-pack/test/functional/services/ml/trained_models_table.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/x-pack/test/functional/services/ml/trained_models_table.ts b/x-pack/test/functional/services/ml/trained_models_table.ts index 91e57f8c63569..941308b61c328 100644 --- a/x-pack/test/functional/services/ml/trained_models_table.ts +++ b/x-pack/test/functional/services/ml/trained_models_table.ts @@ -155,12 +155,6 @@ export function TrainedModelsTableProvider( expectedRow.modelTypes )}' (got '${JSON.stringify(modelRow.modelTypes)}')` ); - // 'Created at' will be different on each run, - // so we will just assert that the value is in the expected timestamp format. - expect(modelRow.createdAt).to.match( - /^\w{3}\s\d+,\s\d{4}\s@\s\d{2}:\d{2}:\d{2}\.\d{3}$/, - `Expected trained model row created at time to have same format as 'Dec 5, 2019 @ 12:28:34.594' (got '${modelRow.createdAt}')` - ); } public async assertTableIsPopulated() { From 6fb1c5ee5c8b8ffd1069a037b6913135b29608b3 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 8 Oct 2024 15:06:30 +0200 Subject: [PATCH 28/30] test action as primary --- .../ml/public/application/model_management/model_actions.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx index 2228ac8b66292..b4ddff093933a 100644 --- a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx +++ b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx @@ -546,8 +546,9 @@ export function useModelActions({ }), 'data-test-subj': 'mlModelsTableRowTestAction', icon: 'inputOutput', - type: 'icon', - isPrimary: false, + // @ts-ignore + type: isMobileLayout ? 'icon' : 'button', + isPrimary: true, available: (item) => isTestable(item, true), onClick: (item) => { if (isDfaTrainedModel(item) && !isBuiltInModel(item)) { From 439c6baa12cfee0d5d6bba3d2ee74f073b21ba07 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 8 Oct 2024 18:30:04 +0200 Subject: [PATCH 29/30] move space --- .../application/model_management/models_list.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index a0ebef1cf981b..f218030c65ad3 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -630,11 +630,14 @@ export const ModelsList: FC = ({ size={'xs'} data-test-subj="mlModelsTableColumnIdValueDescription" > - {descriptionText}  + {descriptionText} {tooltipContent ? ( - - - + <> +   + + + + ) : null} ) : null} From 0812fc8189511b8549bbf664ce0e30e3b5686d50 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 8 Oct 2024 18:51:00 +0200 Subject: [PATCH 30/30] fix ts issue with function overload --- .../application/datavisualizer/data_drift/data_view_editor.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/ml/public/application/datavisualizer/data_drift/data_view_editor.tsx b/x-pack/plugins/ml/public/application/datavisualizer/data_drift/data_view_editor.tsx index 5f52ef1c928f8..eafe31cb0f355 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/data_drift/data_view_editor.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/data_drift/data_view_editor.tsx @@ -80,8 +80,7 @@ export function DataViewEditor({ const { onTableChange, pagination } = useTableSettings( matchedReferenceIndices.length, pageState, - // @ts-expect-error callback will have all the 4 necessary params - updatePageState + updatePageState as Parameters['2'] ); const pageOfItems = useMemo(() => {