Skip to content

Commit

Permalink
[ML] Trained models: Replace download button by extending deploy acti…
Browse files Browse the repository at this point in the history
…on (#205699)

## Summary

* Removes the download model button by extending the deploy action.
* The model download begins automatically after clicking Start
Deployment.
* It is possible to queue one deployment while the model is still
downloading.
* Navigating away from the Trained Models page will not interrupt the
downloading or deployment process.
* `State` column renamed to `Model State`
* Responsiveness fix: icons overlap

https://github.com/user-attachments/assets/045d6f1f-5c2b-4cb5-ad34-ff779add80e3

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
(cherry picked from commit f9c4f59)

# Conflicts:
#	x-pack/platform/plugins/shared/ml/tsconfig.json
  • Loading branch information
rbrtj committed Feb 13, 2025
1 parent f1d1557 commit 09d3898
Show file tree
Hide file tree
Showing 17 changed files with 1,552 additions and 572 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,18 @@ const navigateToUrl = jest.fn().mockImplementation(async (url) => {
describe('useUnsavedChangesPrompt', () => {
let addSpy: jest.SpiedFunction<Window['addEventListener']>;
let removeSpy: jest.SpiedFunction<Window['removeEventListener']>;
let blockSpy: jest.SpiedFunction<CoreScopedHistory['block']>;

beforeEach(() => {
addSpy = jest.spyOn(window, 'addEventListener');
removeSpy = jest.spyOn(window, 'removeEventListener');
blockSpy = jest.spyOn(history, 'block');
});

afterEach(() => {
addSpy.mockRestore();
removeSpy.mockRestore();
blockSpy.mockRestore();
jest.resetAllMocks();
});

Expand Down Expand Up @@ -97,4 +100,23 @@ describe('useUnsavedChangesPrompt', () => {
expect(addSpy).toBeCalledWith('beforeunload', expect.anything());
expect(removeSpy).toBeCalledWith('beforeunload', expect.anything());
});

it('should not block SPA navigation if blockSpaNavigation is false', async () => {
renderHook(() =>
useUnsavedChangesPrompt({
hasUnsavedChanges: true,
blockSpaNavigation: false,
})
);

expect(addSpy).toBeCalledWith('beforeunload', expect.anything());

act(() => history.push('/test'));

expect(coreStart.overlays.openConfirm).not.toBeCalled();

expect(history.location.pathname).toBe('/test');

expect(blockSpy).not.toBeCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ const DEFAULT_CONFIRM_BUTTON = i18n.translate('unsavedChangesPrompt.defaultModal
defaultMessage: 'Leave page',
});

interface Props {
interface BaseProps {
hasUnsavedChanges: boolean;
}

interface SpaBlockingProps extends BaseProps {
http: HttpStart;
openConfirm: OverlayStart['openConfirm'];
history: ScopedHistory;
Expand All @@ -38,20 +41,21 @@ interface Props {
messageText?: string;
cancelButtonText?: string;
confirmButtonText?: string;
blockSpaNavigation?: true;
}

interface BrowserBlockingProps extends BaseProps {
blockSpaNavigation: false;
}

export const useUnsavedChangesPrompt = ({
hasUnsavedChanges,
openConfirm,
history,
http,
navigateToUrl,
// Provide overrides for confirm dialog
messageText = DEFAULT_BODY_TEXT,
titleText = DEFAULT_TITLE_TEXT,
confirmButtonText = DEFAULT_CONFIRM_BUTTON,
cancelButtonText = DEFAULT_CANCEL_BUTTON,
}: Props) => {
type Props = SpaBlockingProps | BrowserBlockingProps;

const isSpaBlocking = (props: Props): props is SpaBlockingProps =>
props.blockSpaNavigation !== false;

export const useUnsavedChangesPrompt = (props: Props) => {
const { hasUnsavedChanges, blockSpaNavigation = true } = props;

useEffect(() => {
if (hasUnsavedChanges) {
const handler = (event: BeforeUnloadEvent) => {
Expand All @@ -67,10 +71,22 @@ export const useUnsavedChangesPrompt = ({
}, [hasUnsavedChanges]);

useEffect(() => {
if (!hasUnsavedChanges) {
if (!hasUnsavedChanges || !isSpaBlocking(props)) {
return;
}

const {
openConfirm,
http,
history,
navigateToUrl,
// Provide overrides for confirm dialog
messageText = DEFAULT_BODY_TEXT,
titleText = DEFAULT_TITLE_TEXT,
confirmButtonText = DEFAULT_CONFIRM_BUTTON,
cancelButtonText = DEFAULT_CANCEL_BUTTON,
} = props;

const unblock = history.block((state) => {
async function confirmAsync() {
const confirmResponse = await openConfirm(messageText, {
Expand All @@ -97,15 +113,5 @@ export const useUnsavedChangesPrompt = ({
});

return unblock;
}, [
history,
hasUnsavedChanges,
openConfirm,
navigateToUrl,
http.basePath,
titleText,
cancelButtonText,
confirmButtonText,
messageText,
]);
}, [hasUnsavedChanges, blockSpaNavigation, props]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -29689,7 +29689,6 @@
"xpack.ml.indexDatavisualizer.actionsPanel.dataframeTitle": "Analyse du cadre de données",
"xpack.ml.inference.modelsList.analyticsMapActionLabel": "Mapping d'analyse",
"xpack.ml.inference.modelsList.analyzeDataDriftLabel": "Analyser la dérive de données",
"xpack.ml.inference.modelsList.downloadModelActionLabel": "Télécharger",
"xpack.ml.inference.modelsList.startModelDeploymentActionDescription": "Démarrer le déploiement",
"xpack.ml.inference.modelsList.startModelDeploymentActionLabel": "Déployer",
"xpack.ml.inference.modelsList.stopModelDeploymentActionLabel": "Arrêter le déploiement",
Expand Down Expand Up @@ -31475,14 +31474,9 @@
"xpack.ml.trainedModels.modelsList.startDeployment.vCULevel": "Sélecteur de niveau des VCU",
"xpack.ml.trainedModels.modelsList.startDeployment.vCUUsageLabel": "Niveau d'utilisation du VCU",
"xpack.ml.trainedModels.modelsList.startDeployment.viewElserDocLink": "Afficher la documentation",
"xpack.ml.trainedModels.modelsList.startFailed": "Impossible de démarrer \"{modelId}\"",
"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.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}\"",
"xpack.ml.trainedModels.modelsList.updateSuccess": "Le déploiement pour \"{modelId}\" a bien été mis à jour.",
"xpack.ml.trainedModels.modelsList.viewTrainingDataActionLabel": "Les données d'entraînement peuvent être consultées lorsque la tâche d'analyse du cadre de données existe.",
"xpack.ml.trainedModels.modelsList.viewTrainingDataNameActionLabel": "Afficher les données d'entraînement",
"xpack.ml.trainedModels.nodesList.adMemoryUsage": "Tâches de détection des anomalies",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29552,7 +29552,6 @@
"xpack.ml.indexDatavisualizer.actionsPanel.dataframeTitle": "データフレーム分析",
"xpack.ml.inference.modelsList.analyticsMapActionLabel": "分析マップ",
"xpack.ml.inference.modelsList.analyzeDataDriftLabel": "データドリフトを分析",
"xpack.ml.inference.modelsList.downloadModelActionLabel": "ダウンロード",
"xpack.ml.inference.modelsList.startModelDeploymentActionDescription": "デプロイを開始",
"xpack.ml.inference.modelsList.startModelDeploymentActionLabel": "デプロイ",
"xpack.ml.inference.modelsList.stopModelDeploymentActionLabel": "デプロイを停止",
Expand Down Expand Up @@ -31336,14 +31335,9 @@
"xpack.ml.trainedModels.modelsList.startDeployment.vCULevel": "VCUレベルセレクター",
"xpack.ml.trainedModels.modelsList.startDeployment.vCUUsageLabel": "VCU使用率レベル",
"xpack.ml.trainedModels.modelsList.startDeployment.viewElserDocLink": "ドキュメンテーションを表示",
"xpack.ml.trainedModels.modelsList.startFailed": "\"{modelId}\"の開始に失敗しました",
"xpack.ml.trainedModels.modelsList.stateHeader": "ステータス",
"xpack.ml.trainedModels.modelsList.stopDeploymentWarning": "\"{deploymentId}\"を停止できませんでした",
"xpack.ml.trainedModels.modelsList.stopFailed": "\"{modelId}\"の停止に失敗しました",
"xpack.ml.trainedModels.modelsList.totalAmountLabel": "学習済みモデルの合計数",
"xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "{modelId}デプロイを更新",
"xpack.ml.trainedModels.modelsList.updateFailed": "\"{modelId}\"を更新できませんでした",
"xpack.ml.trainedModels.modelsList.updateSuccess": "\"{modelId}\"のデプロイが正常に更新されました。",
"xpack.ml.trainedModels.modelsList.viewTrainingDataActionLabel": "データフレーム分析ジョブが存在する場合、学習データを表示できます。",
"xpack.ml.trainedModels.modelsList.viewTrainingDataNameActionLabel": "学習データを表示",
"xpack.ml.trainedModels.nodesList.adMemoryUsage": "異常検知ジョブ",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29639,7 +29639,6 @@
"xpack.ml.indexDatavisualizer.actionsPanel.dataframeTitle": "数据帧分析",
"xpack.ml.inference.modelsList.analyticsMapActionLabel": "分析地图",
"xpack.ml.inference.modelsList.analyzeDataDriftLabel": "分析数据偏移",
"xpack.ml.inference.modelsList.downloadModelActionLabel": "下载",
"xpack.ml.inference.modelsList.startModelDeploymentActionDescription": "开始部署",
"xpack.ml.inference.modelsList.startModelDeploymentActionLabel": "部署",
"xpack.ml.inference.modelsList.stopModelDeploymentActionLabel": "停止部署",
Expand Down
6 changes: 6 additions & 0 deletions x-pack/platform/plugins/shared/ml/common/types/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import type { MlEntityFieldType } from '@kbn/ml-anomaly-utils';
import type { FrozenTierPreference } from '@kbn/ml-date-picker';
import type { StartAllocationParams } from '../../public/application/services/ml_api_service/trained_models';

export const ML_ENTITY_FIELDS_CONFIG = 'ml.singleMetricViewer.partitionFields' as const;
export const ML_APPLY_TIME_RANGE_CONFIG = 'ml.jobSelectorFlyout.applyTimeRange';
Expand All @@ -16,6 +17,7 @@ export const ML_ANOMALY_EXPLORER_PANELS = 'ml.anomalyExplorerPanels';
export const ML_NOTIFICATIONS_LAST_CHECKED_AT = 'ml.notificationsLastCheckedAt';
export const ML_OVERVIEW_PANELS = 'ml.overviewPanels';
export const ML_ELSER_CALLOUT_DISMISSED = 'ml.elserUpdateCalloutDismissed';
export const ML_SCHEDULED_MODEL_DEPLOYMENTS = 'ml.trainedModels.scheduledModelDeployments';

export type PartitionFieldConfig =
| {
Expand Down Expand Up @@ -71,6 +73,7 @@ export interface MlStorageRecord {
[ML_NOTIFICATIONS_LAST_CHECKED_AT]: number | undefined;
[ML_OVERVIEW_PANELS]: OverviewPanelsState;
[ML_ELSER_CALLOUT_DISMISSED]: boolean | undefined;
[ML_SCHEDULED_MODEL_DEPLOYMENTS]: StartAllocationParams[];
}

export type MlStorage = Partial<MlStorageRecord> | null;
Expand All @@ -93,6 +96,8 @@ export type TMlStorageMapped<T extends MlStorageKey> = T extends typeof ML_ENTIT
? OverviewPanelsState | undefined
: T extends typeof ML_ELSER_CALLOUT_DISMISSED
? boolean | undefined
: T extends typeof ML_SCHEDULED_MODEL_DEPLOYMENTS
? string[] | undefined
: null;

export const ML_STORAGE_KEYS = [
Expand All @@ -104,4 +109,5 @@ export const ML_STORAGE_KEYS = [
ML_NOTIFICATIONS_LAST_CHECKED_AT,
ML_OVERVIEW_PANELS,
ML_ELSER_CALLOUT_DISMISSED,
ML_SCHEDULED_MODEL_DEPLOYMENTS,
] as const;
Loading

0 comments on commit 09d3898

Please sign in to comment.