mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
This commit is contained in:
parent
6b9c7586b4
commit
9bdf698bb7
13 changed files with 337 additions and 317 deletions
|
@ -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 & {
|
||||
|
|
|
@ -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(
|
||||
() => ({
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
<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 ? (
|
||||
<>
|
||||
|
||||
<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();
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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": "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向けに最適化",
|
||||
|
@ -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}\"のデプロイが正常に更新されました。",
|
||||
|
|
|
@ -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}”的部署。",
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue