[8.x] [ML] Updates for Trained Models table layout and model states (#194614) (#195546)

This commit is contained in:
Kibana Machine 2024-10-10 06:51:26 +11:00 committed by GitHub
parent 6b9c7586b4
commit 9bdf698bb7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 337 additions and 317 deletions

View file

@ -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 & {

View file

@ -30,9 +30,11 @@ export interface CriteriaWithPagination<T extends object> extends Criteria<T> {
};
}
interface UseTableSettingsReturnValue<T extends object> {
interface UseTableSettingsReturnValue<T extends object, HidePagination extends boolean = false> {
onTableChange: EuiBasicTableProps<T>['onChange'];
pagination: Required<Omit<Pagination, 'showPerPageOptions'>>;
pagination: HidePagination extends true
? Required<Omit<Pagination, 'showPerPageOptions'>> | boolean
: Required<Omit<Pagination, 'showPerPageOptions'>>;
sorting: {
sort: {
field: keyof T;
@ -44,8 +46,31 @@ interface UseTableSettingsReturnValue<T extends object> {
export function useTableSettings<TypeOfItem extends object>(
totalItemCount: number,
pageState: ListingPageUrlState,
updatePageState: (update: Partial<ListingPageUrlState>) => void
): UseTableSettingsReturnValue<TypeOfItem> {
updatePageState: (update: Partial<ListingPageUrlState>) => void,
hide: true
): UseTableSettingsReturnValue<TypeOfItem, true>;
export function useTableSettings<TypeOfItem extends object>(
totalItemCount: number,
pageState: ListingPageUrlState,
updatePageState: (update: Partial<ListingPageUrlState>) => void,
hide?: false
): UseTableSettingsReturnValue<TypeOfItem, false>;
/**
*
* @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<TypeOfItem extends object>(
totalItemCount: number,
pageState: ListingPageUrlState,
updatePageState: (update: Partial<ListingPageUrlState>) => void,
hide: boolean = false
): UseTableSettingsReturnValue<TypeOfItem, boolean> {
const { pageIndex, pageSize, sortField, sortDirection } = pageState;
const onTableChange: EuiBasicTableProps<TypeOfItem>['onChange'] = useCallback(
@ -66,15 +91,19 @@ export function useTableSettings<TypeOfItem extends object>(
[pageState, updatePageState]
);
const pagination = useMemo(
() => ({
const pagination = useMemo(() => {
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;
}
return {
pageIndex,
pageSize,
totalItemCount,
pageSizeOptions: PAGE_SIZE_OPTIONS,
}),
[totalItemCount, pageIndex, pageSize]
);
};
}, [totalItemCount, pageIndex, pageSize, hide]);
const sorting = useMemo(
() => ({

View file

@ -80,8 +80,7 @@ export function DataViewEditor({
const { onTableChange, pagination } = useTableSettings<MatchedItem>(
matchedReferenceIndices.length,
pageState,
// @ts-expect-error callback will have all the 4 necessary params
updatePageState
updatePageState as Parameters<typeof useTableSettings>['2']
);
const pageOfItems = useMemo(() => {

View file

@ -35,7 +35,7 @@ interface DeleteModelsModalProps {
export const DeleteModelsModal: FC<DeleteModelsModalProps> = ({ models, onClose }) => {
const trainedModelsApiService = useTrainedModelsApiService();
const { displayErrorToast, displaySuccessToast } = useToastNotificationService();
const { displayErrorToast } = useToastNotificationService();
const [canDeleteModel, setCanDeleteModel] = useState(false);
const [deletePipelines, setDeletePipelines] = useState<boolean>(false);
@ -66,16 +66,6 @@ export const DeleteModelsModal: FC<DeleteModelsModalProps> = ({ 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,

View file

@ -5,8 +5,17 @@
* 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,
EuiText,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import type { ModelItem } from './models_list';
@ -33,11 +42,11 @@ 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',
}),
@ -46,37 +55,64 @@ export const getModelStateColor = (
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 (
<EuiBadge color={this.color}>
<EuiHealth color={'success'} textSize="xs" css={{ display: 'inline' }}>
{this.name}
</EuiHealth>
</EuiBadge>
);
},
};
case MODEL_STATE.STARTING:
return {
color: 'success',
name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.startingName', {
defaultMessage: 'Starting deployment...',
defaultMessage: 'Deploying',
}),
get component() {
return (
<EuiFlexGroup gutterSize="xs" alignItems="center">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="s" />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="xs">{this.name}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);
},
};
case MODEL_STATE.STOPPING:
return {
color: 'accent',
name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.stoppingName', {
defaultMessage: 'Stopping deployment...',
defaultMessage: 'Stopping',
}),
get component() {
return (
<EuiFlexGroup gutterSize="xs" alignItems="center">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="s" />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="xs">{this.name}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);
},
};
case MODEL_STATE.NOT_DOWNLOADED:
return {
color: '#d4dae5',
name: i18n.translate('xpack.ml.trainedModels.modelsList.modelState.notDownloadedName', {
defaultMessage: 'Not downloaded',
}),
};
default:
return null;
}

View file

@ -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<void>;
modelAndDeploymentIds: string[];
}): Array<Action<ModelItem>> {
const isMobileLayout = useIsWithinMaxBreakpoint('l');
const {
services: {
application: { navigateToUrl },
@ -132,7 +134,7 @@ export function useModelActions({
[]
);
return useMemo(
return useMemo<Array<Action<ModelItem>>>(
() => [
{
name: i18n.translate('xpack.ml.trainedModels.modelsList.viewTrainingDataNameActionLabel', {
@ -203,12 +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 && item.state !== MODEL_STATE.DOWNLOADING;
return canStartStopTrainedModels && !isLoading;
},
available: (item) => {
return (
item.model_type === TRAINED_MODEL_TYPE.PYTORCH && item.state === MODEL_STATE.DOWNLOADED
);
},
available: (item) => item.model_type === TRAINED_MODEL_TYPE.PYTORCH,
onClick: async (item) => {
const modelDeploymentParams = await getUserInputModelDeploymentParams(
item,
@ -234,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(
@ -342,6 +342,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) ||
@ -373,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) {
@ -423,7 +414,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,
@ -480,10 +473,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',
})}
</>
);
@ -491,27 +490,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) => {
@ -539,9 +546,10 @@ export function useModelActions({
}),
'data-test-subj': 'mlModelsTableRowTestAction',
icon: 'inputOutput',
type: 'icon',
// @ts-ignore
type: isMobileLayout ? 'icon' : 'button',
isPrimary: true,
available: isTestable,
available: (item) => isTestable(item, true),
onClick: (item) => {
if (isDfaTrainedModel(item) && !isBuiltInModel(item)) {
onDfaTestAction(item);
@ -550,7 +558,7 @@ export function useModelActions({
}
},
enabled: (item) => {
return canTestTrainedModels && isTestable(item, true) && !isLoading;
return canTestTrainedModels && !isLoading;
},
},
{
@ -612,6 +620,7 @@ export function useModelActions({
trainedModelsApiService,
urlLocator,
onModelDownloadRequest,
isMobileLayout,
]
);
}

View file

@ -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,
@ -16,52 +13,43 @@ import {
EuiCallOut,
EuiFlexGroup,
EuiFlexItem,
EuiHealth,
EuiIcon,
EuiInMemoryTable,
EuiLink,
EuiProgress,
EuiSpacer,
EuiSwitch,
EuiText,
EuiTitle,
EuiToolTip,
type EuiSearchBarProps,
} 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 { dynamic } from '@kbn/shared-ux-utility';
import useMountedState from 'react-use/lib/useMountedState';
import type { ListingPageUrlState } from '@kbn/ml-url-state';
import { getModelStateColor, getModelDeploymentState } from './get_model_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 { 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 { 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 { TestDfaModelsFlyout } from './test_dfa_models_flyout';
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 { useRefresh } from '../routing/use_refresh';
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 { TestModelAndPipelineCreationFlyout } from './test_models';
type Stats = Omit<TrainedModelStat, 'model_id' | 'deployment_stats'>;
@ -106,6 +100,7 @@ export type ModelItem = TrainedModelConfigResponse & {
softwareLicense?: string;
licenseUrl?: string;
downloadState?: ModelDownloadState;
disclaimer?: string;
};
export type ModelItemFull = Required<ModelItem>;
@ -165,8 +160,6 @@ export const ModelsList: FC<Props> = ({
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.
@ -188,7 +181,7 @@ export const ModelsList: FC<Props> = ({
const trainedModelsApiService = useTrainedModelsApiService();
const { displayErrorToast, displaySuccessToast } = useToastNotificationService();
const { displayErrorToast } = useToastNotificationService();
const [isInitialized, setIsInitialized] = useState(false);
const [isLoading, setIsLoading] = useState(false);
@ -219,28 +212,9 @@ export const ModelsList: FC<Props> = ({
}, [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<boolean> => {
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.
@ -288,15 +262,20 @@ export const ModelsList: FC<Props> = ({
const idMap = new Map<string, ModelItem>(
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 }) => {
.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;
})
@ -315,23 +294,28 @@ export const ModelsList: FC<Props> = ({
softwareLicense: modelDefinition.license,
licenseUrl: modelDefinition.licenseUrl,
supported: modelDefinition.supported,
disclaimer: modelDefinition.disclaimer,
} as ModelItem;
});
resultItems = [...resultItems, ...notDownloaded];
}
setItems(resultItems);
if (expandedItemsToRefresh.length > 0) {
await fetchModelsStats(expandedItemsToRefresh);
setItemIdToExpandedRowMap(
expandedItemsToRefresh.reduce((acc, item) => {
acc[item.model_id] = <ExpandedRow item={item as ModelItemFull} />;
return acc;
}, {} as Record<string, JSX.Element>)
);
}
setItems((prevItems) => {
// 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,
...(prevItem?.state === MODEL_STATE.DOWNLOADING
? {
state: prevItem.state,
downloadState: prevItem.downloadState,
}
: {}),
};
});
});
} catch (error) {
displayErrorToast(
error,
@ -340,7 +324,9 @@ export const ModelsList: FC<Props> = ({
})
);
}
setIsInitialized(true);
setIsLoading(false);
await fetchDownloadStatus();
@ -399,20 +385,6 @@ export const ModelsList: FC<Props> = ({
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;
@ -429,6 +401,8 @@ export const ModelsList: FC<Props> = ({
}, []);
const downLoadStatusFetchInProgress = useRef(false);
const abortedDownload = useRef(new Set<string>());
/**
* Updates model list with download status
*/
@ -448,47 +422,43 @@ export const ModelsList: FC<Props> = ({
if (isMounted()) {
setItems((prevItems) => {
return prevItems.map((item) => {
const newItem = { ...item };
if (!item.type?.includes('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<string>(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,
},
})
);
}
Object.keys(downloadStatus).forEach((modelId) => {
if (downloadStatus[modelId]) {
downloadInProgress.add(modelId);
}
});
downloadedModelIds.forEach((v) => {
downloadInProgress.delete(v);
});
if (isEmpty(downloadStatus)) {
downLoadStatusFetchInProgress.current = false;
@ -501,7 +471,7 @@ export const ModelsList: FC<Props> = ({
downLoadStatusFetchInProgress.current = false;
}
},
[trainedModelsApiService, displaySuccessToast, isMounted]
[trainedModelsApiService, isMounted]
);
/**
@ -575,7 +545,6 @@ export const ModelsList: FC<Props> = ({
if (itemIdToExpandedRowMapValues[item.model_id]) {
delete itemIdToExpandedRowMapValues[item.model_id];
} else {
await fetchModelsStats([item]);
itemIdToExpandedRowMapValues[item.model_id] = <ExpandedRow item={item as ModelItemFull} />;
}
setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues);
@ -583,9 +552,8 @@ export const ModelsList: FC<Props> = ({
const columns: Array<EuiBasicTableColumn<ModelItem>> = [
{
align: 'left',
width: '32px',
isExpander: true,
align: 'center',
render: (item: ModelItem) => {
if (!item.stats) {
return null;
@ -610,35 +578,25 @@ export const ModelsList: FC<Props> = ({
},
{
name: modelIdColumnName,
width: '15%',
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,
disclaimer,
}: ModelItem) => {
const isTechPreview = description?.includes('(Tech Preview)');
return (
<EuiFlexGroup gutterSize={'s'} alignItems={'center'} wrap={true}>
<EuiFlexItem grow={false}>{modelId}</EuiFlexItem>
{isTechPreview ? (
<EuiFlexItem grow={false}>
<TechnicalPreviewBadge compressed />
</EuiFlexItem>
) : null}
</EuiFlexGroup>
);
},
},
{
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)', '');
let descriptionText = description?.replace('(Tech Preview)', '');
if (disclaimer) {
descriptionText += '. ' + disclaimer;
}
const tooltipContent =
supported === false ? (
@ -653,80 +611,98 @@ export const ModelsList: FC<Props> = ({
/>
) : null;
return tooltipContent ? (
<EuiToolTip content={tooltipContent}>
<>
{descriptionText}&nbsp;
<EuiIcon type={'warning'} color="warning" />
</>
</EuiToolTip>
) : (
descriptionText
return (
<EuiFlexGroup gutterSize={'xs'} direction={'column'}>
<EuiFlexGroup gutterSize={'s'} alignItems={'center'} wrap={true}>
<EuiFlexItem grow={false}>
<strong data-test-subj="mlModelsTableColumnIdValueId">{modelId}</strong>
</EuiFlexItem>
{isTechPreview ? (
<EuiFlexItem grow={false}>
<TechnicalPreviewBadge compressed />
</EuiFlexItem>
) : null}
</EuiFlexGroup>
{descriptionText ? (
<EuiText
color={'subdued'}
size={'xs'}
data-test-subj="mlModelsTableColumnIdValueDescription"
>
{descriptionText}
{tooltipContent ? (
<>
&nbsp;
<EuiToolTip content={tooltipContent}>
<EuiIcon type={'warning'} color="warning" />
</EuiToolTip>
</>
) : null}
</EuiText>
) : null}
{Array.isArray(type) && type.length > 0 ? (
<EuiFlexGroup gutterSize={'xs'} direction={'row'}>
{type.map((t) => (
<EuiFlexItem key={t} grow={false}>
<span>
<EuiBadge color="hollow" data-test-subj="mlModelType">
{t}
</EuiBadge>
</span>
</EuiFlexItem>
))}
</EuiFlexGroup>
) : null}
</EuiFlexGroup>
);
},
},
{
width: '15%',
field: ModelsTableToConfigMapping.type,
name: i18n.translate('xpack.ml.trainedModels.modelsList.typeHeader', {
defaultMessage: 'Type',
}),
sortable: true,
truncateText: true,
align: 'left',
render: (types: string[]) => (
<EuiFlexGroup gutterSize={'xs'} wrap>
{types.map((type) => (
<EuiFlexItem key={type} grow={false}>
<EuiBadge color="hollow" data-test-subj="mlModelType">
{type}
</EuiBadge>
</EuiFlexItem>
))}
</EuiFlexGroup>
),
'data-test-subj': 'mlModelsTableColumnType',
},
{
width: '10%',
name: i18n.translate('xpack.ml.trainedModels.modelsList.stateHeader', {
defaultMessage: 'State',
}),
align: 'left',
truncateText: false,
width: '150px',
render: ({ state, downloadState }: ModelItem) => {
const config = getModelStateColor(state);
if (!config) return null;
const isDownloadInProgress = state === MODEL_STATE.DOWNLOADING && downloadState;
const isProgressbarVisible = state === MODEL_STATE.DOWNLOADING && downloadState;
const label = (
<EuiHealth textSize={'xs'} color={config.color}>
<EuiText size="xs" color={config.color}>
{config.name}
</EuiHealth>
</EuiText>
);
return (
<EuiFlexGroup direction={'column'} gutterSize={'none'} css={{ width: '100%' }}>
{isDownloadInProgress ? (
{isProgressbarVisible ? (
<EuiFlexItem>
<EuiProgress
label={label}
label={config.name}
valueText={
<>
{((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}
/>
</EuiFlexItem>
) : (
<EuiFlexItem>{label}</EuiFlexItem>
<EuiFlexItem grow={false}>
<span>{config.component ?? label}</span>
</EuiFlexItem>
)}
</EuiFlexGroup>
);
@ -734,21 +710,10 @@ export const ModelsList: FC<Props> = ({
'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', {
defaultMessage: 'Actions',
}),
width: '200px',
actions,
'data-test-subj': 'mlModelsTableColumnActions',
},
@ -836,7 +801,8 @@ export const ModelsList: FC<Props> = ({
const { onTableChange, pagination, sorting } = useTableSettings<ModelItem>(
items.length,
pageState,
updatePageState
updatePageState,
true
);
const search: EuiSearchBarProps = {
@ -921,6 +887,7 @@ export const ModelsList: FC<Props> = ({
<EuiSpacer size="m" />
<div data-test-subj="mlModelsTableContainer">
<EuiInMemoryTable<ModelItem>
tableLayout={'auto'}
responsiveBreakpoint={'xl'}
allowNeutralSort={false}
columns={columns}
@ -974,7 +941,14 @@ export const ModelsList: FC<Props> = ({
{modelsToDelete.length > 0 && (
<DeleteModelsModal
onClose={(refreshList) => {
modelsToDelete.forEach((model) => {
if (model.state === MODEL_STATE.DOWNLOADING) {
abortedDownload.current.add(model.model_id);
}
});
setModelsToDelete([]);
if (refreshList) {
fetchModelsData();
}

View file

@ -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,

View file

@ -30071,7 +30071,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}}",
@ -30086,9 +30085,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",
@ -30126,11 +30123,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...",
@ -30157,14 +30152,10 @@
"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.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.",

View file

@ -29818,7 +29818,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 {パイプライン}}を削除",
@ -29833,9 +29832,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": "E5bidirEctional Encoder rEpresentationsからのEmbEddings",
"xpack.ml.trainedModels.modelsList.e5v1Description": "E5bidirEctional Encoder rEpresentationsからのEmbEddings",
"xpack.ml.trainedModels.modelsList.e5v1x86Description": "E5bidirEctional Encoder rEpresentationsからのEmbEddings、inux-x86_64向けに最適化",
@ -29873,11 +29870,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": "デプロイを停止中...",
@ -29904,13 +29899,10 @@
"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.typeHeader": "型",
"xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "{modelId}デプロイを更新",
"xpack.ml.trainedModels.modelsList.updateFailed": "\"{modelId}\"を更新できませんでした",
"xpack.ml.trainedModels.modelsList.updateSuccess": "\"{modelId}\"のデプロイが正常に更新されました。",

View file

@ -29858,7 +29858,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 {管道}}",
@ -29873,9 +29872,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)",
@ -29913,11 +29910,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": "停止部署......",
@ -29944,14 +29939,10 @@
"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.typeHeader": "类型",
"xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle": "更新 {modelId} 部署",
"xpack.ml.trainedModels.modelsList.updateFailed": "无法更新“{modelId}”",
"xpack.ml.trainedModels.modelsList.updateSuccess": "已成功更新“{modelId}”的部署。",

View file

@ -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'],

View file

@ -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);
@ -161,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() {