diff --git a/x-pack/plugins/search_inference_endpoints/common/constants.ts b/x-pack/plugins/search_inference_endpoints/common/constants.ts
index cdba227626a55b..186901659dd295 100644
--- a/x-pack/plugins/search_inference_endpoints/common/constants.ts
+++ b/x-pack/plugins/search_inference_endpoints/common/constants.ts
@@ -9,3 +9,4 @@ export const PLUGIN_ID = 'searchInferenceEndpoints';
export const PLUGIN_NAME = 'InferenceEndpoints';
export const INFERENCE_ENDPOINTS_QUERY_KEY = 'inferenceEndpointsQueryKey';
+export const TRAINED_MODEL_STATS_QUERY_KEY = 'trainedModelStats';
diff --git a/x-pack/plugins/search_inference_endpoints/common/translations.ts b/x-pack/plugins/search_inference_endpoints/common/translations.ts
index bb04ec80fafdcf..a61d185c406c0a 100644
--- a/x-pack/plugins/search_inference_endpoints/common/translations.ts
+++ b/x-pack/plugins/search_inference_endpoints/common/translations.ts
@@ -119,20 +119,6 @@ export const FORBIDDEN_TO_ACCESS_TRAINED_MODELS = i18n.translate(
}
);
-export const COPY_ID_ACTION_LABEL = i18n.translate(
- 'xpack.searchInferenceEndpoints.actions.copyID',
- {
- defaultMessage: 'Copy endpoint ID',
- }
-);
-
-export const COPY_ID_ACTION_SUCCESS = i18n.translate(
- 'xpack.searchInferenceEndpoints.actions.copyIDSuccess',
- {
- defaultMessage: 'Inference endpoint ID copied!',
- }
-);
-
export const ENDPOINT_ADDED_SUCCESS = i18n.translate(
'xpack.searchInferenceEndpoints.actions.endpointAddedSuccess',
{
@@ -153,13 +139,6 @@ export const ENDPOINT_ADDED_SUCCESS_DESCRIPTION = (endpointId: string) =>
values: { endpointId },
});
-export const DELETE_ACTION_LABEL = i18n.translate(
- 'xpack.searchInferenceEndpoints.actions.deleteSingleEndpoint',
- {
- defaultMessage: 'Delete endpoint',
- }
-);
-
export const ENDPOINT = i18n.translate('xpack.searchInferenceEndpoints.endpoint', {
defaultMessage: 'Endpoint',
});
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/use_copy_id_action.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/copy_id_action.test.tsx
similarity index 80%
rename from x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/use_copy_id_action.test.tsx
rename to x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/copy_id_action.test.tsx
index 1445e0c41c5745..288ba5089d3673 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/use_copy_id_action.test.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/copy_id_action.test.tsx
@@ -8,7 +8,7 @@
import { renderReactTestingLibraryWithI18n as render } from '@kbn/test-jest-helpers';
import React from 'react';
import { useKibana } from '../../../../../../hooks/use_kibana';
-import { useCopyIDAction } from './use_copy_id_action';
+import { CopyIDAction } from './copy_id_action';
const mockInferenceEndpoint = {
deployment: 'not_applicable',
@@ -35,8 +35,6 @@ Object.defineProperty(navigator, 'clipboard', {
configurable: true,
});
-const mockOnActionSuccess = jest.fn();
-
jest.mock('../../../../../../hooks/use_kibana', () => ({
useKibana: jest.fn(),
}));
@@ -53,21 +51,19 @@ const addSuccess = jest.fn();
},
}));
-describe('useCopyIDAction hook', () => {
+describe('CopyIDAction', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('renders the label with correct text', () => {
const TestComponent = () => {
- const { getAction } = useCopyIDAction({ onActionSuccess: mockOnActionSuccess });
- const action = getAction(mockInferenceEndpoint);
- return
{action}
;
+ return ;
};
const { getByTestId } = render();
const labelElement = getByTestId('inference-endpoints-action-copy-id-label');
- expect(labelElement).toHaveTextContent('Copy endpoint ID');
+ expect(labelElement).toBeVisible();
});
});
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/copy_id_action.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/copy_id_action.tsx
new file mode 100644
index 00000000000000..4641899b59720c
--- /dev/null
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/copy_id_action.tsx
@@ -0,0 +1,47 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { EuiButtonIcon, EuiCopy } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import React from 'react';
+import { useKibana } from '../../../../../../hooks/use_kibana';
+
+interface CopyIDActionProps {
+ modelId: string;
+}
+
+export const CopyIDAction = ({ modelId }: CopyIDActionProps) => {
+ const {
+ services: { notifications },
+ } = useKibana();
+ const toasts = notifications?.toasts;
+
+ return (
+
+ {(copy) => (
+ {
+ copy();
+ toasts?.addSuccess({
+ title: i18n.translate('xpack.searchInferenceEndpoints.actions.copyIDSuccess', {
+ defaultMessage: 'Inference endpoint ID {modelId} copied',
+ values: { modelId },
+ }),
+ });
+ }}
+ size="s"
+ />
+ )}
+
+ );
+};
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/use_copy_id_action.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/use_copy_id_action.tsx
deleted file mode 100644
index b43b308af068a0..00000000000000
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/use_copy_id_action.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { EuiContextMenuItem, EuiCopy, EuiIcon } from '@elastic/eui';
-import React from 'react';
-import * as i18n from '../../../../../../../common/translations';
-import { useKibana } from '../../../../../../hooks/use_kibana';
-import { InferenceEndpointUI } from '../../../../types';
-import { UseCopyIDActionProps } from '../types';
-
-export const useCopyIDAction = ({ onActionSuccess }: UseCopyIDActionProps) => {
- const {
- services: { notifications },
- } = useKibana();
- const toasts = notifications?.toasts;
-
- const getAction = (inferenceEndpoint: InferenceEndpointUI) => {
- return (
-
- {(copy) => (
- }
- onClick={() => {
- copy();
- onActionSuccess();
- toasts?.addSuccess({ title: i18n.COPY_ID_ACTION_SUCCESS });
- }}
- size="s"
- >
- {i18n.COPY_ID_ACTION_LABEL}
-
- )}
-
- );
- };
-
- return { getAction };
-};
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.test.tsx
index e6f4594e2d0cde..8fe5c0e94b0584 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.test.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.test.tsx
@@ -18,7 +18,7 @@ describe('ConfirmDeleteEndpointModal', () => {
render();
});
- it('renders the modal with correct texts', () => {
+ it('renders the modal with correct elements', () => {
expect(screen.getByText(i18n.DELETE_TITLE)).toBeInTheDocument();
expect(screen.getByText(i18n.CONFIRM_DELETE_WARNING)).toBeInTheDocument();
expect(screen.getByText(i18n.CANCEL)).toBeInTheDocument();
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts
index 6b2dff7d8b8f1d..4e306afcc3ac84 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts
@@ -22,3 +22,10 @@ export const CONFIRM_DELETE_WARNING = i18n.translate(
'Deleting an active endpoint will cause operations targeting associated semantic_text fields and inference pipelines to fail.',
}
);
+
+export const DELETE_ACTION_LABEL = i18n.translate(
+ 'xpack.searchInferenceEndpoints.actions.deleteSingleEndpoint',
+ {
+ defaultMessage: 'Delete endpoint',
+ }
+);
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/delete_action.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/delete_action.tsx
new file mode 100644
index 00000000000000..88a982fd0b76e0
--- /dev/null
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/delete_action.tsx
@@ -0,0 +1,55 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { EuiButtonIcon } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import React, { useState } from 'react';
+import { useDeleteEndpoint } from '../../../../../../hooks/use_delete_endpoint';
+import { InferenceEndpointUI } from '../../../../types';
+import { ConfirmDeleteEndpointModal } from './confirm_delete_endpoint';
+
+interface DeleteActionProps {
+ selectedEndpoint?: InferenceEndpointUI;
+}
+
+export const DeleteAction: React.FC = ({ selectedEndpoint }) => {
+ const [isModalVisible, setIsModalVisible] = useState(false);
+
+ const { mutate: deleteEndpoint } = useDeleteEndpoint(() => setIsModalVisible(false));
+
+ const onConfirmDeletion = () => {
+ if (!selectedEndpoint) {
+ return;
+ }
+
+ deleteEndpoint({
+ type: selectedEndpoint.type,
+ id: selectedEndpoint.endpoint.model_id,
+ });
+ };
+
+ return (
+ <>
+ setIsModalVisible(true)}
+ />
+ {isModalVisible && (
+ setIsModalVisible(false)}
+ onConfirm={onConfirmDeletion}
+ />
+ )}
+ >
+ );
+};
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/use_delete_action.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/use_delete_action.tsx
deleted file mode 100644
index 1a2e5cdb5b51f4..00000000000000
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/use_delete_action.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { EuiContextMenuItem, EuiIcon } from '@elastic/eui';
-import React, { useCallback, useState } from 'react';
-import * as i18n from '../../../../../../../common/translations';
-import { useDeleteEndpoint } from '../../../../../../hooks/use_delete_endpoint';
-import { InferenceEndpointUI } from '../../../../types';
-import type { UseActionProps } from '../types';
-
-export const useDeleteAction = ({ onActionSuccess }: UseActionProps) => {
- const [isModalVisible, setIsModalVisible] = useState(false);
- const [endpointToBeDeleted, setEndpointToBeDeleted] = useState(null);
- const onCloseModal = useCallback(() => setIsModalVisible(false), []);
- const openModal = useCallback(
- (selectedEndpoint: InferenceEndpointUI) => {
- onActionSuccess();
- setIsModalVisible(true);
- setEndpointToBeDeleted(selectedEndpoint);
- },
- [onActionSuccess]
- );
-
- const { mutate: deleteEndpoint } = useDeleteEndpoint();
-
- const onConfirmDeletion = useCallback(() => {
- onCloseModal();
- if (!endpointToBeDeleted) {
- return;
- }
-
- deleteEndpoint({
- type: endpointToBeDeleted.type,
- id: endpointToBeDeleted.endpoint.model_id,
- });
- }, [deleteEndpoint, onCloseModal, endpointToBeDeleted]);
-
- const getAction = (selectedEndpoint: InferenceEndpointUI) => {
- return (
- }
- onClick={() => openModal(selectedEndpoint)}
- >
- {i18n.DELETE_ACTION_LABEL}
-
- );
- };
-
- return { getAction, isModalVisible, onConfirmDeletion, onCloseModal };
-};
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/types.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/types.ts
deleted file mode 100644
index a80ccae703b7af..00000000000000
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/types.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-export interface UseActionProps {
- onActionSuccess: () => void;
-}
-
-export type UseCopyIDActionProps = Pick;
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/use_actions.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/use_actions.tsx
deleted file mode 100644
index f76d147c68646b..00000000000000
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/use_actions.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { EuiTableComputedColumnType } from '@elastic/eui';
-import { EuiButtonIcon, EuiContextMenuPanel, EuiPopover } from '@elastic/eui';
-import React, { useCallback, useState } from 'react';
-import { InferenceEndpointUI } from '../../types';
-import { useCopyIDAction } from './actions/copy_id/use_copy_id_action';
-import { ConfirmDeleteEndpointModal } from './actions/delete/confirm_delete_endpoint';
-import { useDeleteAction } from './actions/delete/use_delete_action';
-
-export const ActionColumn: React.FC<{ interfaceEndpoint: InferenceEndpointUI }> = ({
- interfaceEndpoint,
-}) => {
- const [isPopoverOpen, setIsPopoverOpen] = useState(false);
- const tooglePopover = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]);
- const closePopover = useCallback(() => setIsPopoverOpen(false), []);
-
- const copyIDAction = useCopyIDAction({
- onActionSuccess: closePopover,
- });
-
- const deleteAction = useDeleteAction({
- onActionSuccess: closePopover,
- });
-
- const items = [
- copyIDAction.getAction(interfaceEndpoint),
- deleteAction.getAction(interfaceEndpoint),
- ];
-
- return (
- <>
-
- }
- isOpen={isPopoverOpen}
- closePopover={closePopover}
- panelPaddingSize="none"
- anchorPosition="downLeft"
- >
-
-
- {deleteAction.isModalVisible ? (
-
- ) : null}
- >
- );
-};
-
-interface UseBulkActionsReturnValue {
- actions: EuiTableComputedColumnType;
-}
-
-export const useActions = (): UseBulkActionsReturnValue => {
- return {
- actions: {
- align: 'right',
- render: (interfaceEndpoint: InferenceEndpointUI) => {
- return ;
- },
- width: '165px',
- },
- };
-};
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/deployment_status.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/deployment_status.tsx
index 61e6e620d24856..cd2466e483ac26 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/deployment_status.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/deployment_status.tsx
@@ -15,7 +15,7 @@ interface DeploymentStatusProps {
}
export const DeploymentStatus: React.FC = ({ status }) => {
- if (status === DeploymentStatusEnum.notApplicable) {
+ if (status === DeploymentStatusEnum.notApplicable || !status) {
return null;
}
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.tsx
index 25892236d61f75..1cb34a06ff9978 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.tsx
@@ -21,18 +21,18 @@ export interface EndpointInfoProps {
export const EndpointInfo: React.FC = ({ endpoint }) => {
return (
-
-
+
+
{endpoint.model_id}
-
+
);
};
-export const EndpointModelInfo: React.FC = ({ endpoint }) => {
+const EndpointModelInfo: React.FC = ({ endpoint }) => {
const serviceSettings = endpoint.service_settings;
const modelId =
'model_id' in serviceSettings
@@ -44,15 +44,10 @@ export const EndpointModelInfo: React.FC = ({ endpoint }) =>
const isEligibleForMITBadge = modelId && ELASTIC_MODEL_DEFINITIONS[modelId]?.license === 'MIT';
return (
-
- {modelId && (
-
-
-
- )}
-
- {isEligibleForMITBadge && (
-
+ <>
+
+ {modelId && }
+ {isEligibleForMITBadge ? (
= ({ endpoint }) =>
>
{i18n.MIT_LICENSE}
-
- )}
-
-
-
- {endpointModelAtrributes(endpoint)}
-
-
-
+ ) : null}{' '}
+ {endpointModelAtrributes(endpoint)}
+
+ >
);
};
@@ -141,7 +131,7 @@ function openAIAttributes(endpoint: InferenceAPIConfigResponse) {
function azureOpenAIStudioAttributes(endpoint: InferenceAPIConfigResponse) {
const serviceSettings = endpoint.service_settings;
- const provider = 'provider' in serviceSettings ? serviceSettings.provider : undefined;
+ const provider = 'provider' in serviceSettings ? serviceSettings?.provider : undefined;
const endpointType =
'endpoint_type' in serviceSettings ? serviceSettings.endpoint_type : undefined;
const target = 'target' in serviceSettings ? serviceSettings.target : undefined;
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/table_columns.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/table_columns.tsx
deleted file mode 100644
index 1b53aeb6ada996..00000000000000
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/table_columns.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils';
-import React from 'react';
-import type { HorizontalAlignment } from '@elastic/eui';
-import { TaskTypes } from '../../../../common/types';
-import * as i18n from '../../../../common/translations';
-import { useActions } from './render_actions/use_actions';
-import { EndpointInfo } from './render_endpoint/endpoint_info';
-import { ServiceProvider } from './render_service_provider/service_provider';
-import { TaskType } from './render_task_type/task_type';
-import { DeploymentStatus } from './render_deployment_status/deployment_status';
-import { DeploymentStatusEnum, ServiceProviderKeys } from '../types';
-
-export const useTableColumns = () => {
- const { actions } = useActions();
- const deploymentAlignment: HorizontalAlignment = 'center';
-
- const TABLE_COLUMNS = [
- {
- field: 'deployment',
- name: '',
- render: (deployment: DeploymentStatusEnum) => {
- if (deployment != null) {
- return ;
- }
-
- return null;
- },
- width: '64px',
- align: deploymentAlignment,
- },
- {
- field: 'endpoint',
- name: i18n.ENDPOINT,
- render: (endpoint: InferenceAPIConfigResponse) => {
- if (endpoint != null) {
- return ;
- }
-
- return null;
- },
- sortable: true,
- },
- {
- field: 'provider',
- name: i18n.SERVICE_PROVIDER,
- render: (provider: ServiceProviderKeys) => {
- if (provider != null) {
- return ;
- }
-
- return null;
- },
- sortable: false,
- width: '185px',
- },
- {
- field: 'type',
- name: i18n.TASK_TYPE,
- render: (type: TaskTypes) => {
- if (type != null) {
- return ;
- }
-
- return null;
- },
- sortable: false,
- width: '185px',
- },
- actions,
- ];
-
- return TABLE_COLUMNS;
-};
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.test.tsx
index 91a2ea959fdec4..70791287d9ae1a 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.test.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.test.tsx
@@ -10,6 +10,8 @@ import { screen } from '@testing-library/react';
import { render } from '@testing-library/react';
import { TabularPage } from './tabular_page';
import { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils';
+import { TRAINED_MODEL_STATS_QUERY_KEY } from '../../../common/constants';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const inferenceEndpoints = [
{
@@ -43,8 +45,15 @@ jest.mock('../../hooks/use_delete_endpoint', () => ({
}));
describe('When the tabular page is loaded', () => {
+ const queryClient = new QueryClient();
+ queryClient.setQueryData([TRAINED_MODEL_STATS_QUERY_KEY], {
+ trained_model_stats: [{ model_id: '.elser_model_2', deployment_stats: { state: 'started' } }],
+ });
+ const wrapper = ({ children }: { children: React.ReactNode }) => {
+ return {children};
+ };
beforeEach(() => {
- render();
+ render(wrapper({ children: }));
});
it('should display all model_ids in the table', () => {
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.tsx
index ec19ad49ad477b..cb3b8856594173 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.tsx
@@ -5,25 +5,34 @@
* 2.0.
*/
-import React, { useCallback, useEffect } from 'react';
+import React, { useCallback } from 'react';
-import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import {
+ EuiBasicTable,
+ EuiBasicTableColumn,
+ EuiFlexGroup,
+ EuiFlexItem,
+ HorizontalAlignment,
+} from '@elastic/eui';
import { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils';
-import { extractErrorProperties } from '@kbn/ml-error-utils';
+import { TaskTypes } from '../../../common/types';
import * as i18n from '../../../common/translations';
import { useTableData } from '../../hooks/use_table_data';
-import { FilterOptions } from './types';
+import { FilterOptions, InferenceEndpointUI, ServiceProviderKeys } from './types';
import { DeploymentStatusEnum } from './types';
import { useAllInferenceEndpointsState } from '../../hooks/use_all_inference_endpoints_state';
-import { EndpointsTable } from './endpoints_table';
import { ServiceProviderFilter } from './filter/service_provider_filter';
import { TaskTypeFilter } from './filter/task_type_filter';
import { TableSearch } from './search/table_search';
-import { useTableColumns } from './render_table_columns/table_columns';
-import { useKibana } from '../../hooks/use_kibana';
+import { DeploymentStatus } from './render_table_columns/render_deployment_status/deployment_status';
+import { EndpointInfo } from './render_table_columns/render_endpoint/endpoint_info';
+import { ServiceProvider } from './render_table_columns/render_service_provider/service_provider';
+import { TaskType } from './render_table_columns/render_task_type/task_type';
+import { DeleteAction } from './render_table_columns/render_actions/actions/delete/delete_action';
+import { CopyIDAction } from './render_table_columns/render_actions/actions/copy_id/copy_id_action';
interface TabularPageProps {
inferenceEndpoints: InferenceAPIConfigResponse[];
@@ -31,16 +40,9 @@ interface TabularPageProps {
export const TabularPage: React.FC = ({ inferenceEndpoints }) => {
const [searchKey, setSearchKey] = React.useState('');
- const [deploymentStatus, setDeploymentStatus] = React.useState<
- Record
- >({});
const { queryParams, setQueryParams, filterOptions, setFilterOptions } =
useAllInferenceEndpointsState();
- const {
- services: { ml, notifications },
- } = useKibana();
-
const onFilterChangedCallback = useCallback(
(newFilterOptions: Partial) => {
setFilterOptions(newFilterOptions);
@@ -48,43 +50,76 @@ export const TabularPage: React.FC = ({ inferenceEndpoints })
[setFilterOptions]
);
- useEffect(() => {
- const fetchDeploymentStatus = async () => {
- const trainedModelStats = await ml?.mlApi?.trainedModels.getTrainedModelStats();
- if (trainedModelStats) {
- const newDeploymentStatus = trainedModelStats?.trained_model_stats.reduce(
- (acc, modelStat) => {
- if (modelStat.model_id) {
- acc[modelStat.model_id] =
- modelStat?.deployment_stats?.state === 'started'
- ? DeploymentStatusEnum.deployed
- : DeploymentStatusEnum.notDeployed;
- }
- return acc;
- },
- {} as Record
- );
- setDeploymentStatus(newDeploymentStatus);
- }
- };
-
- fetchDeploymentStatus().catch((error) => {
- const errorObj = extractErrorProperties(error);
- notifications?.toasts?.addError(errorObj.message ? new Error(error.message) : error, {
- title: i18n.TRAINED_MODELS_STAT_GATHER_FAILED,
- });
- });
- }, [ml, notifications]);
-
const { paginatedSortedTableData, pagination, sorting } = useTableData(
inferenceEndpoints,
queryParams,
filterOptions,
- searchKey,
- deploymentStatus
+ searchKey
);
- const tableColumns = useTableColumns();
+ const tableColumns: Array> = [
+ {
+ field: 'deployment',
+ name: '',
+ render: (deployment: DeploymentStatusEnum) => ,
+ align: 'center' as HorizontalAlignment,
+ width: '64px',
+ },
+ {
+ field: 'endpoint',
+ name: i18n.ENDPOINT,
+ render: (endpoint: InferenceAPIConfigResponse) => {
+ if (endpoint) {
+ return ;
+ }
+
+ return null;
+ },
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ field: 'provider',
+ name: i18n.SERVICE_PROVIDER,
+ render: (provider: ServiceProviderKeys) => {
+ if (provider) {
+ return ;
+ }
+
+ return null;
+ },
+ sortable: false,
+ width: '185px',
+ },
+ {
+ field: 'type',
+ name: i18n.TASK_TYPE,
+ render: (type: TaskTypes) => {
+ if (type) {
+ return ;
+ }
+
+ return null;
+ },
+ sortable: false,
+ width: '185px',
+ },
+ {
+ actions: [
+ {
+ render: (inferenceEndpoint: InferenceEndpointUI) => (
+
+ ),
+ },
+ {
+ render: (inferenceEndpoint: InferenceEndpointUI) => (
+
+ ),
+ },
+ ],
+ width: '165px',
+ },
+ ];
const handleTableChange = useCallback(
({ page, sort }) => {
@@ -123,9 +158,10 @@ export const TabularPage: React.FC = ({ inferenceEndpoints })
- {
- const { inferenceEndpoints } = useQueryInferenceEndpoints();
+ const { data } = useQueryInferenceEndpoints();
+
+ const inferenceEndpoints = data || [];
return (
<>
diff --git a/x-pack/plugins/search_inference_endpoints/public/hooks/use_delete_endpoint.tsx b/x-pack/plugins/search_inference_endpoints/public/hooks/use_delete_endpoint.tsx
index aee9fa1dc55ca1..b8a12d8238f9c2 100644
--- a/x-pack/plugins/search_inference_endpoints/public/hooks/use_delete_endpoint.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/hooks/use_delete_endpoint.tsx
@@ -17,7 +17,7 @@ interface MutationArgs {
id: string;
}
-export const useDeleteEndpoint = () => {
+export const useDeleteEndpoint = (onSuccess?: () => void) => {
const queryClient = useQueryClient();
const { services } = useKibana();
const toasts = services.notifications?.toasts;
@@ -32,6 +32,9 @@ export const useDeleteEndpoint = () => {
toasts?.addSuccess({
title: i18n.DELETE_SUCCESS,
});
+ if (onSuccess) {
+ onSuccess();
+ }
},
onError: (error: { body: KibanaServerError }) => {
toasts?.addError(new Error(error.body.message), {
diff --git a/x-pack/plugins/search_inference_endpoints/public/hooks/use_inference_endpoints.ts b/x-pack/plugins/search_inference_endpoints/public/hooks/use_inference_endpoints.ts
index f400429bec2507..1a6435cd25153b 100644
--- a/x-pack/plugins/search_inference_endpoints/public/hooks/use_inference_endpoints.ts
+++ b/x-pack/plugins/search_inference_endpoints/public/hooks/use_inference_endpoints.ts
@@ -11,13 +11,10 @@ import { APIRoutes } from '../types';
import { useKibana } from './use_kibana';
import { INFERENCE_ENDPOINTS_QUERY_KEY } from '../../common/constants';
-export const useQueryInferenceEndpoints = (): {
- inferenceEndpoints: InferenceAPIConfigResponse[];
- isLoading: boolean;
-} => {
+export const useQueryInferenceEndpoints = () => {
const { services } = useKibana();
- const { data, isLoading } = useQuery({
+ return useQuery({
queryKey: [INFERENCE_ENDPOINTS_QUERY_KEY],
queryFn: async () => {
const response = await services.http.get<{
@@ -27,6 +24,4 @@ export const useQueryInferenceEndpoints = (): {
return response.inference_endpoints;
},
});
-
- return { inferenceEndpoints: data || [], isLoading };
};
diff --git a/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.test.tsx b/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.test.tsx
index a8d0326a4c36fd..df058df72fb42d 100644
--- a/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.test.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.test.tsx
@@ -10,6 +10,9 @@ import { renderHook } from '@testing-library/react-hooks';
import { QueryParams } from '../components/all_inference_endpoints/types';
import { useTableData } from './use_table_data';
import { INFERENCE_ENDPOINTS_TABLE_PER_PAGE_VALUES } from '../components/all_inference_endpoints/types';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import React from 'react';
+import { TRAINED_MODEL_STATS_QUERY_KEY } from '../../common/constants';
const inferenceEndpoints = [
{
@@ -59,17 +62,23 @@ const filterOptions = {
type: ['sparse_embedding', 'text_embedding'],
} as any;
-const deploymentStatus = {
- '.elser_model_2': 'deployed',
- lang_ident_model_1: 'not_deployed',
-} as any;
-
const searchKey = 'my';
describe('useTableData', () => {
+ const queryClient = new QueryClient();
+ const wrapper = ({ children }: { children: React.ReactNode }) => {
+ return {children};
+ };
+
+ beforeEach(() => {
+ queryClient.setQueryData([TRAINED_MODEL_STATS_QUERY_KEY], {
+ trained_model_stats: [{ model_id: '.elser_model_2', deployment_stats: { state: 'started' } }],
+ });
+ });
it('should return correct pagination', () => {
- const { result } = renderHook(() =>
- useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey, deploymentStatus)
+ const { result } = renderHook(
+ () => useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey),
+ { wrapper }
);
expect(result.current.pagination).toEqual({
@@ -81,8 +90,9 @@ describe('useTableData', () => {
});
it('should return correct sorting', () => {
- const { result } = renderHook(() =>
- useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey, deploymentStatus)
+ const { result } = renderHook(
+ () => useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey),
+ { wrapper }
);
expect(result.current.sorting).toEqual({
@@ -94,8 +104,9 @@ describe('useTableData', () => {
});
it('should return correctly sorted data', () => {
- const { result } = renderHook(() =>
- useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey, deploymentStatus)
+ const { result } = renderHook(
+ () => useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey),
+ { wrapper }
);
const expectedSortedData = [...inferenceEndpoints].sort((a, b) =>
@@ -113,8 +124,9 @@ describe('useTableData', () => {
provider: ['elser'],
type: ['text_embedding'],
} as any;
- const { result } = renderHook(() =>
- useTableData(inferenceEndpoints, queryParams, filterOptions2, searchKey, deploymentStatus)
+ const { result } = renderHook(
+ () => useTableData(inferenceEndpoints, queryParams, filterOptions2, searchKey),
+ { wrapper }
);
const filteredData = result.current.sortedTableData;
@@ -129,16 +141,18 @@ describe('useTableData', () => {
it('should filter data based on searchKey', () => {
const searchKey2 = 'model-05';
- const { result } = renderHook(() =>
- useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey2, deploymentStatus)
+ const { result } = renderHook(
+ () => useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey2),
+ { wrapper }
);
const filteredData = result.current.sortedTableData;
expect(filteredData.every((item) => item.endpoint.model_id.includes(searchKey))).toBeTruthy();
});
it('should update deployment status based on deploymentStatus object', () => {
- const { result } = renderHook(() =>
- useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey, deploymentStatus)
+ const { result } = renderHook(
+ () => useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey),
+ { wrapper }
);
const updatedData = result.current.sortedTableData;
diff --git a/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.tsx b/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.tsx
index 663df5547c600f..1b3ce7814891f7 100644
--- a/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.tsx
@@ -20,6 +20,7 @@ import {
ServiceProviderKeys,
} from '../components/all_inference_endpoints/types';
import { DeploymentStatusEnum } from '../components/all_inference_endpoints/types';
+import { useTrainedModelStats } from './use_trained_model_stats';
interface UseTableDataReturn {
tableData: InferenceEndpointUI[];
@@ -33,9 +34,20 @@ export const useTableData = (
inferenceEndpoints: InferenceAPIConfigResponse[],
queryParams: QueryParams,
filterOptions: FilterOptions,
- searchKey: string,
- deploymentStatus: Record
+ searchKey: string
): UseTableDataReturn => {
+ const { data: trainedModelStats } = useTrainedModelStats();
+
+ const deploymentStatus = trainedModelStats?.trained_model_stats.reduce((acc, modelStat) => {
+ if (modelStat.model_id) {
+ acc[modelStat.model_id] =
+ modelStat?.deployment_stats?.state === 'started'
+ ? DeploymentStatusEnum.deployed
+ : DeploymentStatusEnum.notDeployed;
+ }
+ return acc;
+ }, {} as Record);
+
const tableData: InferenceEndpointUI[] = useMemo(() => {
let filteredEndpoints = inferenceEndpoints;
@@ -62,7 +74,7 @@ export const useTableData = (
if (isElasticService) {
const modelId = endpoint.service_settings?.model_id;
deploymentStatusValue =
- modelId && deploymentStatus[modelId] !== undefined
+ modelId && deploymentStatus?.[modelId]
? deploymentStatus[modelId]
: DeploymentStatusEnum.notDeployable;
}
diff --git a/x-pack/plugins/search_inference_endpoints/public/hooks/use_trained_model_stats.ts b/x-pack/plugins/search_inference_endpoints/public/hooks/use_trained_model_stats.ts
new file mode 100644
index 00000000000000..57b643b78dc3a4
--- /dev/null
+++ b/x-pack/plugins/search_inference_endpoints/public/hooks/use_trained_model_stats.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { useQuery } from '@tanstack/react-query';
+import { InferenceStatsResponse } from '@kbn/ml-plugin/public/application/services/ml_api_service/trained_models';
+import { useKibana } from './use_kibana';
+import { TRAINED_MODEL_STATS_QUERY_KEY } from '../../common/constants';
+
+export const useTrainedModelStats = () => {
+ const { services } = useKibana();
+
+ return useQuery({
+ queryKey: [TRAINED_MODEL_STATS_QUERY_KEY],
+ queryFn: async () => {
+ const response = await services.ml?.mlApi?.trainedModels.getTrainedModelStats();
+
+ return response || ({ count: 0, trained_model_stats: [] } as InferenceStatsResponse);
+ },
+ });
+};
diff --git a/x-pack/plugins/search_inference_endpoints/tsconfig.json b/x-pack/plugins/search_inference_endpoints/tsconfig.json
index 6094924306e541..e915df9529b6b4 100644
--- a/x-pack/plugins/search_inference_endpoints/tsconfig.json
+++ b/x-pack/plugins/search_inference_endpoints/tsconfig.json
@@ -4,9 +4,9 @@
"outDir": "target/types",
},
"include": [
- "__mocks__/**/*",
- "common/**/*",
- "public/**/*",
+ "__mocks__/**/*",
+ "common/**/*",
+ "public/**/*",
"server/**/*"
],
"kbn_references": [
@@ -29,7 +29,6 @@
"@kbn/doc-links",
"@kbn/console-plugin",
"@kbn/test-jest-helpers",
- "@kbn/ml-error-utils",
"@kbn/kibana-utils-plugin"
],
"exclude": [